diff --git a/.env.example b/.env.example index becf631..fdb2d3f 100644 --- a/.env.example +++ b/.env.example @@ -33,7 +33,7 @@ BDAG_NODE_ARCHIVAL=0 # Dashboard repository. Release builds always use the main branch. DASHBOARD_REPO=https://github.com/BlockdagEngineering/redis-dash.git -P2P_PORT=8150 +P2P_PORT=8155 BDAG_RPC_PORT=38131 EVM_HTTP_PORT=18545 EVM_WS_PORT=18546 @@ -45,12 +45,14 @@ DASHBOARD_HOST_PORT=8088 # Dashboard UI display timezone. DASHBOARD_TIMEZONE=Africa/Johannesburg -# NODE_DATA_DIR=./node-data +NODE_DATA_DIR=./data/node -# Required for any pool/miner deployment. Do not use the zero address. +# Required for any pool/miner deployment. Do not use the zero address. Leave +# blank in the example so release/install readiness fails until an operator +# supplies the payout address for this deployment. # POOL_COINBASE_ADDRESS may be set separately when it must differ from the # displayed miner/pool identity; otherwise set MINING_POOL_ADDRESS only. -MINING_POOL_ADDRESS=0xEd5e2dD1377aBe28907673cdEa8c2c04Fb715dcd +MINING_POOL_ADDRESS= POOL_COINBASE_ADDRESS= # Storage placement for constrained mining appliances. Release installers keep @@ -87,9 +89,12 @@ BDAG_P2P_PROTOCOLS=tcp # Standard mining-host performance defaults. The release topology has one # production node with a direct pool-to-node RPC path. BDAG_NODE_CACHE_MB=2048 +BDAG_NODE_DEBUG_LEVEL=warn +BDAG_NODE_NO_FILE_LOGGING=1 BDAG_EVM_CACHE_MB=2048 BDAG_NODE_CACHE_DATABASE_PERCENT=70 BDAG_NODE_OBSOLETE_HEIGHT=20 +BDAG_NODE_MINING_NO_PENDING_TX=0 BDAG_BLOCK_READ_AHEAD_KB=1024 BDAG_BLOCK_NR_REQUESTS=256 BDAG_NODE_CPU_SHARES=6144 @@ -127,6 +132,8 @@ POOL_GBT_PRESSURE_INTERVAL_MS=500 POOL_GBT_PRESSURE_WINDOW_SECONDS=10 POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15 POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30 +POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15 +POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2 # Host network discovery is only for choosing this node's advertised P2P address. # Peer candidates should be complete P2P multiaddrs. @@ -134,6 +141,9 @@ BDAG_NETWORK_TOPOLOGY=auto BDAG_DETECTED_NETWORK_TOPOLOGY= BDAG_ASIC_LAN_INTERFACE= BDAG_ASIC_LAN_CIDRS= +POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp +POOL_ASIC_MAC_ALLOWLIST= +POOL_ASIC_MAC_OVERRIDES= # Optional route to miners on a remote LAN reached through a routed gateway # (e.g. a ZeroTier peer). When set, the miner-route compose service keeps the # host route alive so the watchdog can reach the miner HTTP API. Leave empty @@ -151,9 +161,14 @@ BDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1 BDAG_CHAIN_PEERSTORE_LOG_TAIL=8000 EXTRA_PEER_ADDRESSES= -# When enabled by installer/watchdog for attached ASICs, this must include -# --miner, --miningaddr=, and --maxinbound=1 on constrained -# USB/router hosts. Do not add --allowminingwhennearlysynced or +# Node template generation is enabled for this mining stack. When +# BDAG_NODE_MINING_ARGS is blank, the node entrypoint derives +# --miner --miningaddr= at container +# start. Catch-up repairs may explicitly set BDAG_ENABLE_NODE_MINING=0. +BDAG_ENABLE_NODE_MINING=0 +BDAG_NODE_MODULES=Blockdag,miner +# When set explicitly, this must include --miner and +# --miningaddr=. Do not add --allowminingwhennearlysynced or # --allowsubmitwhennotsynced; template/readiness health must fail closed when # P2P freshness or sync safety is unavailable. BDAG_NODE_MINING_ARGS= @@ -175,6 +190,7 @@ BDAG_ADAPTIVE_CHAIN_RPC_WARN_MS=1000 BDAG_STATUS_SAMPLER_ENABLED=1 BDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10 BDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120 +BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS=300 BDAG_CHAIN_STATE_SELF_HEAL_ENABLED=1 BDAG_CHAIN_STATE_SELF_HEAL_UNIT=bdag-chain-state-self-heal.service BDAG_MINING_IMPERATIVE_CHAIN_STATE_RESTORE_ENABLED=1 @@ -196,11 +212,12 @@ BDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60 BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60 BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS=60 BDAG_DASHBOARD_GLOBAL_SAMPLER_INTERVAL_SECONDS=60 +BDAG_EVM_SYNC_BACKOFF_SECONDS=60 BDAG_OPS_UID=1000 BDAG_OPS_GID=1000 BDAG_DOCKER_SOCKET_GID=0 BDAG_GLOBAL_CACHE_TTL_SECONDS=60 -BDAG_GLOBAL_RPC_WORKERS=24 +BDAG_GLOBAL_RPC_WORKERS=4 BDAG_GLOBAL_BLOCK_WINDOW=600 BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30 BDAG_INSTALL_REBUILD_DASHBOARD_PLOTS=1 @@ -424,18 +441,30 @@ MINER_WORKERS=1 -# Conservative block-candidate policy for constrained nodes. Keep template -# refresh/recovery slower than the node RPC client ceiling can absorb. +# ASIC block-candidate timing pinned to the best observed production window. POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true -POOL_TEMPLATE_TTL_REFRESH_MS=2000 +POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false +POOL_TEMPLATE_TTL_REFRESH_MS=100 POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false -POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=2500 +POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750 +POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100 +POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100 +POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true +POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000 POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true POOL_STALE_RACE_REJECT_WINDOW_SECONDS=10 POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1 +POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0 POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5 POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3 POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120 POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60 POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true +POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0 +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20 +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000 diff --git a/README.md b/README.md index 1fe7926..8a1c74b 100644 --- a/README.md +++ b/README.md @@ -291,12 +291,41 @@ is behind peers or while the selected backend is not mineable/submit-ready. using `iowait`, `io_some`, and `io_full` pressure signals; a production node more than `BDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300` blocks behind peers is the backup trigger when pressure signals are missing or delayed. -The status sampler stops the pool, disables node mining/template runtime churn, -raises the node cache toward `BDAG_CATCHUP_NODE_CACHE_MB` within the host memory -budget, and recreates only the node service when that runtime change is needed. -The dashboard reports this as a deliberate catch-up pause, not a pool failure, -and tells operators to leave miners configured until I/O pressure drops, peer lag -is back inside the safe window, and template health is ready. +The status sampler leaves the pool container running for share/session +continuity, pauses pool-side template work, disables node mining/template runtime +churn, and may raise the node cache toward `BDAG_CATCHUP_NODE_CACHE_MB` within +the host memory budget. It must not recreate the node while the pool is live or +accepted-block history exists. The dashboard reports this as a deliberate +catch-up pause, not a pool failure, and tells operators to leave miners +configured until I/O pressure drops, peer lag is back inside the safe window, and +template health is ready. + +Native P2P freshness remains the mining hard gate. A zero-peer native P2P sample +blocks new mining work immediately, but `BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS=300` +prevents the watchdog from restarting the node until peer loss is sustained and +fresh paid-block evidence has expired. Chain-state restore is stricter than +catch-up display state: it requires native peer-lag evidence and ignores +EVM/public-reference lag by itself. + +EVM/public-reference lag is advisory only after native mining proof is complete: +`getTemplateHealth` or backend metrics must prove current chain, fresh P2P, +fresh consensus peer floor, peer lead inside the configured safety window, +template readiness, and submit readiness. Unknown peer count or unknown peer +lead fails closed. When miners are connected, zero ready miner lanes keeps +`can_submit_blocks=false` unless fresh paid-block evidence proves the submit +path is already converting work. + +Node config edits and service recreates are cold/idle operations. Before the +pool is live, automation may prepare missing node mining/template support only +after native safety gates pass. Once `pool` is running, or once any +accepted-block history exists, automation must not rewrite node +mining/template flags, peer lists, cache settings, or recreate `node`. Recovery +should fail closed by pausing templates, preserving the existing node +identity/data path, and waiting for native `getTemplateHealth` to prove +`chain_current`, `p2p_mining_fresh`, `mineable_now`, and `submit_ready`. If +chain data must be restored, stop the mining path deliberately, quarantine the +old datadir, preserve `network.key`, restore a verified raw datadir or +snapshot, and restart the existing `node` container without a Compose recreate. Dashboard block height is sourced from chain RPC `getBlockCount`; template height, logs, and main-order values are shown only as diff --git a/docker-compose.yml b/docker-compose.yml index 650fe7a..d0841ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -99,10 +99,20 @@ services: TMP: /tmp TEMP: /tmp BDAG_NETWORK: ${BDAG_NETWORK:-} - P2P_PORT: ${P2P_PORT:-8150} + P2P_PORT: ${P2P_PORT:-8155} BDAG_EPHEMERAL_DIR: /run/bdag-ephemeral BDAG_SNAPSHOT_URL: ${BDAG_SNAPSHOT_URL:-} BDAG_NODE_ARCHIVAL: ${BDAG_NODE_ARCHIVAL:-0} + BDAG_NODE_DEBUG_LEVEL: ${BDAG_NODE_DEBUG_LEVEL:-warn} + BDAG_NODE_NO_FILE_LOGGING: ${BDAG_NODE_NO_FILE_LOGGING:-1} + BDAG_NODE_OBSOLETE_HEIGHT: ${BDAG_NODE_OBSOLETE_HEIGHT:-20} + BDAG_NODE_MINING_NO_PENDING_TX: ${BDAG_NODE_MINING_NO_PENDING_TX:-0} + BDAG_ENABLE_NODE_MINING: ${BDAG_ENABLE_NODE_MINING:-0} + BDAG_NODE_MODULES: ${BDAG_NODE_MODULES:-Blockdag,miner} + BDAG_NODE_MINING_ARGS: ${BDAG_NODE_MINING_ARGS:-} + MINING_POOL_ADDRESS: ${MINING_POOL_ADDRESS:?set MINING_POOL_ADDRESS to a non-zero payout address} + POOL_COINBASE_ADDRESS: ${POOL_COINBASE_ADDRESS:-} + MINING_ADDRESS: ${MINING_ADDRESS:-} NODE_ARGS_APPEND: ${NODE_ARGS_APPEND:-} SYNC_SOURCE_NODE: ${SYNC_SOURCE_NODE:-0} BDAG_STORAGE_PROFILE: ${BDAG_STORAGE_PROFILE:-auto} @@ -121,7 +131,7 @@ services: # so if you want to start fresh, you need to delete the volume explicity. # `docker compose down && docker volume rm _node-data`\ - - ${NODE_DATA_DIR:-node-data}:/var/lib/bdagStack/node + - ${NODE_DATA_DIR:-./data/node}:/var/lib/bdagStack/node - nodeworker-data:/var/lib/bdagStack/nodeworker - ${SNAPSHOT_HOST_PATH:-./docker/no-snapshot.marker}:/snapshot/latest.bdsnap:ro # node.conf in project root (copy from node.conf.example). gitignored. @@ -169,24 +179,26 @@ services: POOL_FEE_PERCENTAGE: ${POOL_FEE_PERCENTAGE:-1.0} POOL_BLOCK_MATURITY: ${POOL_BLOCK_MATURITY:-10} POOL_PAYOUT_MATURITY: ${POOL_PAYOUT_MATURITY:-4096} + POOL_PAYOUT_MAX_BLOCKS_PER_TICK: ${POOL_PAYOUT_MAX_BLOCKS_PER_TICK:-5} + POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS: ${POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS:-60} POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: ${POOL_RPC_ROUTER_NODE_HEALTH_ENABLED:-true} POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS: ${POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS:-15} POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS: ${POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS:-30} POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS: ${POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS:-15} POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS: ${POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS:-2} - POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: ${POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED:-true} + POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: ${POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED:-false} POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS: ${POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS:-30} POOL_RPC_ROUTER_EVM_REFERENCE_URLS: ${POOL_RPC_ROUTER_EVM_REFERENCE_URLS:-https://rpc.blockdag.engineering,https://rpc.bdagscan.com} - POOL_TEMPLATE_TTL_REFRESH_MS: ${POOL_TEMPLATE_TTL_REFRESH_MS:-2000} + POOL_TEMPLATE_TTL_REFRESH_MS: ${POOL_TEMPLATE_TTL_REFRESH_MS:-100} POOL_SUBMIT_STALE_BLOCK_CANDIDATES: ${POOL_SUBMIT_STALE_BLOCK_CANDIDATES:-false} - POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: ${POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS:-250} - POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS: ${POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS:-2500} + POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: ${POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS:-1750} + POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS: ${POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS:-1750} POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE: ${POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE:-true} POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS: ${POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS:-8000} POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: ${POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED:-true} POOL_STALE_RACE_REJECT_WINDOW_SECONDS: ${POOL_STALE_RACE_REJECT_WINDOW_SECONDS:-10} POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD:-1} - POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-2} + POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0} POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS: ${POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS:-120} POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS: ${POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS:-60} POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS: ${POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS:-5} @@ -203,10 +215,10 @@ services: POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS: ${POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS:-60} POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES: ${POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES:-20} POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT: ${POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT:-5} - POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS:-250} - POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS:-2500} + POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS:-1750} + POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS:-1750} POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS:-100} - POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS:-1000} + POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS: ${POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS:-100} POOL_BLOCK_TIMING_CONTROLLER_STEP_MS: ${POOL_BLOCK_TIMING_CONTROLLER_STEP_MS:-250} POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS: ${POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS:-20} BDAG_ASIC_LAN_CIDRS: ${BDAG_ASIC_LAN_CIDRS:-} @@ -272,6 +284,7 @@ services: NODE_RPC_USER: ${NODE_RPC_USER:-test} NODE_RPC_PASS: ${NODE_RPC_PASS:-test} BDAG_WATCHDOG_INTERVAL: ${BDAG_WATCHDOG_INTERVAL:-60} + BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS: ${BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS:-300} volumes: - /var/run/docker.sock:/var/run/docker.sock - ${BDAG_STACK_HOST_ROOT:-${PWD}}:${BDAG_STACK_HOST_ROOT:-${PWD}} @@ -431,6 +444,7 @@ services: - /run/dashboard-redis:size=16m,mode=0700 volumes: - dashboard-redis:/var/lib/dashboard-redis + - ${BDAG_HOST_ARP_TABLE_PATH:-/proc/net/arp}:/host/proc/net/arp:ro depends_on: node: { condition: service_started } pool: { condition: service_started } @@ -451,12 +465,13 @@ services: BDAG_REDIS_MAXMEMORY_POLICY: ${BDAG_REDIS_MAXMEMORY_POLICY:-noeviction} BDAG_EVM_HTTP_URL: ${BDAG_EVM_HTTP_URL:-http://host.docker.internal:18545} BDAG_EVM_WS_URL: ${BDAG_EVM_WS_URL:-ws://host.docker.internal:18546} + BDAG_EVM_SYNC_BACKOFF_SECONDS: ${BDAG_EVM_SYNC_BACKOFF_SECONDS:-60} BDAG_POOL_METRICS_URL: ${BDAG_POOL_METRICS_URL:-http://host.docker.internal:9090/metrics} BDAG_NODE_RPC_URL: ${BDAG_NODE_RPC_URL:-http://host.docker.internal:38131} BDAG_GLOBAL_BLOCK_WINDOW: ${BDAG_GLOBAL_BLOCK_WINDOW:-600} BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS: ${BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS:-30} BDAG_GLOBAL_FAST_WARMUP_BLOCKS: ${BDAG_GLOBAL_FAST_WARMUP_BLOCKS:-120} - BDAG_GLOBAL_RPC_WORKERS: ${BDAG_GLOBAL_RPC_WORKERS:-24} + BDAG_GLOBAL_RPC_WORKERS: ${BDAG_GLOBAL_RPC_WORKERS:-4} BDAG_GLOBAL_SETTLE_DELAY_SECONDS: ${BDAG_GLOBAL_SETTLE_DELAY_SECONDS:-5} BDAG_GLOBAL_MAX_INCREMENTAL_GAP: ${BDAG_GLOBAL_MAX_INCREMENTAL_GAP:-120} BDAG_GLOBAL_RPC_CHUNK_SIZE: ${BDAG_GLOBAL_RPC_CHUNK_SIZE:-64} @@ -478,7 +493,9 @@ services: DASHBOARD_TIMEZONE: ${DASHBOARD_TIMEZONE:-Africa/Johannesburg} BDAG_MINER_CONTROL_ENABLED: ${BDAG_MINER_CONTROL_ENABLED:-1} BDAG_ASIC_LAN_CIDRS: ${BDAG_ASIC_LAN_CIDRS:-} + POOL_ASIC_ARP_TABLE_PATH: ${POOL_ASIC_ARP_TABLE_PATH:-/host/proc/net/arp} POOL_ASIC_MAC_OVERRIDES: ${POOL_ASIC_MAC_OVERRIDES:-} + POOL_ASIC_MAC_ALLOWLIST: ${POOL_ASIC_MAC_ALLOWLIST:-} BDAG_POOL_URL: ${BDAG_POOL_URL:-} MINING_POOL_ADDRESS: ${MINING_POOL_ADDRESS:?set MINING_POOL_ADDRESS to a non-zero payout address} POOL_COINBASE_ADDRESS: ${POOL_COINBASE_ADDRESS:-} diff --git a/docker/entrypoint-nodeworker.sh b/docker/entrypoint-nodeworker.sh index b8d6c23..e3e1939 100644 --- a/docker/entrypoint-nodeworker.sh +++ b/docker/entrypoint-nodeworker.sh @@ -127,10 +127,10 @@ valid_tcp_port() { } configured_p2p_port() { - local port="${P2P_PORT:-8150}" + local port="${P2P_PORT:-8155}" if ! valid_tcp_port "$port"; then - log "invalid P2P_PORT=$port; using release default P2P_PORT=8150" - port=8150 + log "invalid P2P_PORT=$port; using release default P2P_PORT=8155" + port=8155 fi printf '%s\n' "$port" } @@ -360,6 +360,20 @@ node_args_contains_prefix() { return 1 } +configured_node_mining_address() { + local value + for value in "${POOL_COINBASE_ADDRESS:-}" "${MINING_POOL_ADDRESS:-}" "${MINING_ADDRESS:-}"; do + value="${value//[[:space:]]/}" + [ -n "$value" ] || continue + printf '%s' "$value" | grep -Eiq '^0x[0-9a-f]{40}$' || continue + if [ "$(lower_ascii "$value")" != "0x0000000000000000000000000000000000000000" ]; then + printf '%s\n' "$value" + return 0 + fi + done + return 1 +} + append_node_arg_prefix_once() { local flag="$1" local node_args="$2" @@ -371,6 +385,12 @@ append_node_arg_prefix_once() { export NODE_ARGS_APPEND } +forbidden_node_module() { + local module_name + module_name="$(lower_ascii "$1")" + [ "$module_name" = "$(printf 'p%s' '2p')" ] +} + rewrite_node_args_configfile() { local node_args="$1" local config_file="$2" @@ -443,16 +463,29 @@ apply_node_mining_runtime_args() { *) return 0 ;; esac - local node_args modules word + local node_args modules word mining_address node_args="$(node_args_from_argv "$@" || true)" + if [ -z "${BDAG_NODE_MINING_ARGS:-}" ]; then + if mining_address="$(configured_node_mining_address)"; then + BDAG_NODE_MINING_ARGS="--miner --miningaddr=${mining_address}" + export BDAG_NODE_MINING_ARGS + log "derived BDAG_NODE_MINING_ARGS from configured pool payout address" + else + log "BDAG_ENABLE_NODE_MINING=1 but no valid non-zero pool payout address was provided; node mining args left unchanged" + fi + fi modules="${BDAG_NODE_MODULES:-}" if [ -n "$modules" ]; then - modules="$(printf '%s' "$modules" | tr ',' ' ')" - for word in $modules; do - [ -n "$word" ] || continue - append_node_arg_once "--modules=${word}" "$node_args ${NODE_ARGS_APPEND:-}" - done - fi + modules="$(printf '%s' "$modules" | tr ',' ' ')" + for word in $modules; do + [ -n "$word" ] || continue + if forbidden_node_module "$word"; then + log "BDAG_NODE_MODULES contains a forbidden peer module; mining nodes must use Blockdag,miner only" + exit 1 + fi + append_node_arg_once "--modules=${word}" "$node_args ${NODE_ARGS_APPEND:-}" + done + fi for word in ${BDAG_NODE_MINING_ARGS:-}; do case "$word" in --miningaddr=*) append_node_arg_prefix_once "$word" "$node_args ${NODE_ARGS_APPEND:-}" ;; @@ -461,6 +494,56 @@ apply_node_mining_runtime_args() { done } +apply_node_mining_template_runtime_args() { + local node_args obsolete_height no_pending + node_args="$(node_args_from_argv "$@" || true)" + + obsolete_height="${BDAG_NODE_OBSOLETE_HEIGHT:-}" + if [ -n "$obsolete_height" ]; then + case "$obsolete_height" in + *[!0-9]*) + log "ignoring invalid BDAG_NODE_OBSOLETE_HEIGHT=$obsolete_height" + ;; + *) + append_node_arg_prefix_once "--obsoleteheight=$obsolete_height" "$node_args ${NODE_ARGS_APPEND:-}" + ;; + esac + fi + + no_pending="${BDAG_NODE_MINING_NO_PENDING_TX:-0}" + case "$no_pending" in + 1|true|TRUE|yes|YES|on|ON) + append_node_arg_once "--miningnopendingtx" "$node_args ${NODE_ARGS_APPEND:-}" + ;; + esac +} + +apply_node_log_runtime_args() { + local node_args level normalized_level no_file_logging + node_args="$(node_args_from_argv "$@" || true)" + level="${BDAG_NODE_DEBUG_LEVEL:-warn}" + normalized_level="$(lower_ascii "$level")" + case "$normalized_level" in + ""|default|none|off|disable|disabled) + ;; + trace|debug|info|warn|error|critical) + append_node_arg_prefix_once "--debuglevel=$normalized_level" "$node_args ${NODE_ARGS_APPEND:-}" + ;; + *) + log "invalid BDAG_NODE_DEBUG_LEVEL=$level; using warn" + append_node_arg_prefix_once "--debuglevel=warn" "$node_args ${NODE_ARGS_APPEND:-}" + ;; + esac + + no_file_logging="${BDAG_NODE_NO_FILE_LOGGING:-1}" + if env_value_true "$no_file_logging"; then + append_node_arg_once "--nofilelogging" "$node_args ${NODE_ARGS_APPEND:-}" + elif ! env_value_false "$no_file_logging"; then + log "invalid BDAG_NODE_NO_FILE_LOGGING=$no_file_logging; using enabled" + append_node_arg_once "--nofilelogging" "$node_args ${NODE_ARGS_APPEND:-}" + fi +} + mount_source_for_path() { local path="$1" real best_src="" best_target="" src target fstype rest real="$(readlink -m "$path" 2>/dev/null || printf '%s' "$path")" @@ -867,7 +950,9 @@ maybe_http_snapshot_bootstrap() { apply_ordered_fastsync_peers "$@" apply_no_fastsync_serve_guard "$@" +apply_node_mining_template_runtime_args "$@" apply_node_mining_runtime_args "$@" +apply_node_log_runtime_args "$@" apply_archival_flag "$@" maybe_http_snapshot_bootstrap "$@" diff --git a/docs/asic-performance-drift-gates.md b/docs/asic-performance-drift-gates.md new file mode 100644 index 0000000..9f51ca2 --- /dev/null +++ b/docs/asic-performance-drift-gates.md @@ -0,0 +1,20 @@ +# ASIC Performance Drift Gates + +Mining optimization changes must preserve enough data to prove whether the pool is improving or drifting. + +## Required signals + +- Per-ASIC block-candidate attribution must be present in `block_submissions`. +- Each attributed row must include `asic_mac`, `lane_id`, `lane_identity_source`, `worker`, `job_id`, `protocol_variant`, `backend_attempted`, `pdiff`, `share_diff`, and `job_age_ms`. +- The pool must expose current ASIC performance through `/health/asic-performance`. +- The dashboard Status tab must render ASIC performance from Redis key `bdag:asic:performance`. +- Comparisons against other pools or older tuning states must use windows of at least 30 minutes unless the result is explicitly marked as a short diagnostic sample. +- Cross-pool comparisons must be normalized by active X100 count/hash power. Local pool `0x1719...e7a0` has 4 X100s; comparison pool `0xd5f8...b1f5` has 2 X100s. Treat raw local block output as needing roughly 2x `b1f5` before calling performance comparable. Use paid blocks per X100-hour for optimization decisions. + +## Regression gates + +- `pool` tests must verify block-submission insert columns, attribution helper behavior, and ASIC performance rollups. +- `redis-dash` tests must verify Redis/runtime ASIC merge behavior, Status row/chart data, and Status template rendering. +- `stack-redis` release readiness must fail if Postgres is missing ASIC attribution columns or lane indexes. + +These gates are intended to prevent a repeat of the blind spot where aggregate pool blocks were visible but per-ASIC paid-block contribution could not be reconstructed after container rotation. diff --git a/docs/mining-appliance-optimization.md b/docs/mining-appliance-optimization.md index cd2592c..653c3a9 100644 --- a/docs/mining-appliance-optimization.md +++ b/docs/mining-appliance-optimization.md @@ -13,8 +13,10 @@ The Compose files use: - Lower CPU and block IO weights for dashboard/control-plane services. - Large `nofile` limits for node and pool sockets. - Graceful stop windows for node and database shutdown. -- Node cache, BD cache, DAG cache, reduced log verbosity, and no file logging - via `NODE_ARGS_APPEND`. +- Node cache tuning plus reduced node log verbosity and no node file logging. + The entrypoint adds `--debuglevel=${BDAG_NODE_DEBUG_LEVEL:-warn}` and + `--nofilelogging` by default so catch-up does not write one Docker/file log + record per imported block. For the production node, apply: @@ -33,7 +35,7 @@ environment: --cache=${BDAG_NODE_CACHE_MB:-4096} --bdcachesize=${BDAG_NODE_BD_CACHE_SIZE:-8192} --dagcachesize=${BDAG_NODE_DAG_CACHE_SIZE:-8192} - --debuglevel=${BDAG_NODE_DEBUG_LEVEL:-error} + --debuglevel=${BDAG_NODE_DEBUG_LEVEL:-warn} --evmtrietimeout=${BDAG_EVM_TRIE_TIMEOUT_SECONDS:-7200} --nofilelogging ``` diff --git a/docs/redis-dash-deploy-difficulties-2026-06-19.md b/docs/redis-dash-deploy-difficulties-2026-06-19.md index 7f88f91..ffd255e 100644 --- a/docs/redis-dash-deploy-difficulties-2026-06-19.md +++ b/docs/redis-dash-deploy-difficulties-2026-06-19.md @@ -159,8 +159,29 @@ Mitigations now required by source: - Chain-state self-heal stops/parks mining work, quarantines the stale node data, restores from a configured trusted source or snapshot, restarts node/dashboard, and leaves the pool stopped until readiness gates pass. -- Mining must never be resumed merely because native P2P is fresh if local EVM - remains syncing or materially behind public reference heads. +- Local EVM sync lag is advisory when native template health and pool backend + metrics prove mining safety (`mineable`, `submit_ready`, `p2p_mining_fresh`, + zero peer lead). It becomes a hard blocker only when native safety is missing + or public/reference evidence shows explicit divergence or solo-mining risk. + +### Dashboard EVM Polling Must Back Off During Sync + +After node logging was reduced, the remaining avoidable pressure came from +redis-dash EVM polling and trend backfill retrying while the EVM gateway was +still syncing or had no active relay peer. That produced repeated gateway +warnings and extra node work without improving mining readiness. + +Mitigations now required by source: + +- Redis-dash treats explicit EVM sync/no-relay responses as a cooldown signal + and pauses live polling, warmup, and trend catch-up during + `BDAG_EVM_SYNC_BACKOFF_SECONDS` instead of hammering `eth_getBlockByNumber`. +- The first `eth_getBlockByNumber(latest)` sync/no-peer failure is terminal for + that pass; redis-dash must not immediately fall through to `eth_blockNumber` + and another `latest` request. +- Stack defaults set `BDAG_EVM_SYNC_BACKOFF_SECONDS=60`, which keeps the UI + responsive when EVM is healthy while protecting the native mining path during + catch-up. ### Peer Lists And Peerstores Need Durable Service Endpoints @@ -193,6 +214,185 @@ source of truth is: - direct container/process state: `docker ps`, listening ports, and recent `pool`/`node` logs. +`backend_mineable` and `backend_submit_ready` can briefly drop to zero during +parent/template invalidation immediately after accepted blocks. Redis-dash +therefore records a per-sample `accepted_block_submissions_delta` from the live +pool metrics stream. A fresh accepted-block delta may bridge that transient only +when P2P freshness, peer lead, ready miners, and template age are also safe; it +must not be replaced with a lifetime accepted-block total. + +ASIC MAC identity must not be pinned to stale IP addresses. During the live +recovery, `POOL_ASIC_MAC_OVERRIDES` still mapped a previous DHCP assignment, so +two active Stratum lanes were reported with the same MAC even though the host ARP +table had the correct current IP-to-MAC mapping. Pool releases must prefer a +complete live ARP/neighbor entry over an override and use overrides only as a +fallback when the container cannot see LAN neighbors. + During the fixed run, the pool correctly moved from zero ready miners while the node was behind peers to four ready miners after native health returned `submit_ready=true`. + +### 2026-06-21 Live Node Mutation Incident + +The stack later proved a separate automation hazard. While the pool had already +accepted thousands of blocks, the status sampler saw ASIC demand and enabled +node mining/template support. The accepted-block "recent" check had aged out by +roughly 70 seconds, so the repair path edited runtime config and recreated the +`node` service. The recreated node exposed near-genesis chain data even though +the previous node had been mining close to the live chain, forcing the pool into +`node_syncing` and correctly pausing jobs. + +Root failure: + +- paid work was treated as recent-only, not as durable evidence that the node is + production-active; +- catch-up/runtime and node-template repairs were allowed to mutate node config + before proving the pool was cold or idle; +- Compose recreate was used where preserving the existing node process identity + and datadir view mattered more than applying a convenience config repair. + +Mitigations now required by source: + +- Any running `pool` container, recent paid work, or lifetime accepted-block + evidence blocks node config edits and `node` recreates from status-sampler + automation. Cold/idle setup may prepare missing node mining/template support + before the pool is live, but only after native safety gates pass. +- `recreate_node_services()` is the central guard for status-sampler node + recreates. Repair paths must call it rather than constructing their own + Compose recreate command. +- Catch-up runtime adjustment may pause templates, but it must not rewrite node + mining flags, cache settings, peer lists, or recreate `node` while a live pool + or accepted-block history exists. +- Chain restore is an explicit operator/self-heal flow: stop the mining path, + quarantine the old datadir, preserve `network.key`, restore a verified + datadir/snapshot, start the existing node, and require native + `getTemplateHealth` to prove `chain_current`, `p2p_mining_fresh`, + `mineable_now`, and `submit_ready` before mining resumes. +- Release tests must include stale-but-present paid-work evidence. A lifetime + accepted-block count is enough to block live node mutation even when the last + accepted block is outside the short freshness window. +- EVM/public-reference lag and cached catch-up state are advisory only when + native mining proof is complete: current chain, fresh P2P, fresh consensus + peer floor, safe peer lead, template readiness, and submit readiness. Unknown + peer count or unknown peer lead fails closed. +- A selected backend may not be treated as mining-safe from `p2p_mining_fresh` + alone. Backend metrics must also include enough peer-count and peer-lead + evidence, and connected miners with zero ready lanes keep + `can_submit_blocks=false` unless fresh paid-block evidence proves the submit + path is already working. +- `repair_hold` is not a blanket permission model. It remains a hard block for + node restarts, node/container recreates, and config edits, but it may carry an + explicit allow-list for pool and ASIC recovery actions so a stale incident hold + cannot prevent mining recovery after native safety is proven. The pool start + gate still makes the final native-safety decision. + +### ASIC Pool APIs Can Wedge While Controllers Stay Alive + +X100 controllers can still answer `/mcb/status` and `/mcb/setting` while the +pool/cgminer endpoint `/mcb/pools` times out or returns errors. Treating that +as "not a miner" hides a recoverable ASIC and leaves pool operators looking at +only the surviving miners. + +Mitigations now required by source: + +- Miner discovery keeps an ASIC visible when status/settings identify it but + the pool API is wedged. The scan row includes `pool_api_error`, an empty + pool list, and `active=false` instead of disappearing. +- MAC address remains the canonical ASIC identity. DHCP/IP reuse after restart + is expected; do not key miner health, retirement, or restart decisions by IP. +- When native/pool health is good but an ASIC has controller responses and no + pool API, restart that ASIC controller first. Do not restart node or pool for + this symptom. + +### Payout Balance Log Flood Can Steal Catch-Up I/O + +When the payout wallet has zero balance and a large mature backlog exists, the +pool can repeatedly log payout-processing and insufficient-balance lines for +every eligible block. This does not fix payouts, but it does add Docker log I/O +while the node is trying to catch up. + +Mitigations now required by source: + +- The pool checks wallet balance before logging that a payout block is being + processed. +- The pool defers payout transaction submission unless the selected native + backend is mining-safe: fresh node health, submit-ready templates, safe P2P, + and the same native predicate that allows mining. +- The lifecycle loop stops scanning further payout blocks after the first + insufficient-balance proof in a tick, because later blocks cannot be paid + until the wallet balance changes. +- Payout backlog processing is capped by + `POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5` by default so catch-up payouts cannot + monopolize RPC and log I/O after the native backend is safe enough to accept + payout traffic. Set it to `0` only when explicitly choosing unlimited payout + catch-up over mining-node recovery. +- Repeated insufficient-balance warnings are throttled and summarized while + real payout send failures and database update failures remain immediate. + +### Stratum Retry Logs Can Steal Catch-Up I/O + +When the backend is syncing and templates are intentionally unavailable, X100 +ASICs repeatedly reconnect and retry normal subscribe/authorize flows. Logging +every accepted socket, request, authorization, and short EOF can create heavy +Docker log churn without making the node catch up faster. + +Mitigations now required by source: + +- The pool throttles routine Stratum connection logs by event type, ASIC lane, + and detail with `POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60` by default. +- The throttle is logging-only. Stratum behavior, accepted shares, block + submissions, invalid JSON, unsupported methods, authorization failures, and + socket read errors keep their existing behavior. +- Set `POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=0` only during focused + Stratum debugging when every retry line is worth the extra log I/O. + +### Node Import Logs Can Steal Catch-Up I/O + +When the node runs at the upstream default `debuglevel=info`, catch-up emits an +`Imported new chain segment` line for almost every imported block. On a +USB-backed mining appliance this produced roughly a thousand Docker log lines +per minute while the chain was still behind peers, adding avoidable disk I/O to +the same host that needed to catch up before mining could resume. + +Mitigations now required by source: + +- The node entrypoint defaults `BDAG_NODE_DEBUG_LEVEL=warn` and + `BDAG_NODE_NO_FILE_LOGGING=1`, appending `--debuglevel=warn` and + `--nofilelogging` independently of `NODE_ARGS_APPEND`. +- `NODE_ARGS_APPEND` is still reserved for operator/mining flags. Runtime code + quotes env-file values with spaces so generated `.env` files remain + sourceable by install/support scripts. +- The mining-appliance preflight warns when an install is configured for + high-volume node logging. + +### Stale Repair Holds Can Block The Only Useful Sync Repair + +On 2026-06-21 the live node stopped advancing while peer tips kept moving. The +pool correctly withheld jobs because native readiness reported node syncing and +unsafe P2P freshness, but the automation control file was still in +`repair_hold` from a previous incident. That hold allowed pool/ASIC recovery +only, so node restart and chain-state self-heal attempts were repeatedly +denied while the pool stayed idle. + +Recovery that worked: + +- Prove the node is stuck with two direct `eth_syncing` samples. The bad state + was `currentBlock` unchanged while `highestBlock` increased. +- Clear the stale automation hold only after confirming there is no recent + paid work and the pool is already withholding jobs. +- Restart the node without deleting or recreating the datadir. In this + incident the peer lead fell from roughly 6259 blocks to zero in minutes. +- Recreate the pool after pinning the best observed ASIC timing so the pool + starts with `POOL_TEMPLATE_TTL_REFRESH_MS=100`, + `POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750`, + `POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750`, and multiple block + candidates enabled. + +Mitigations now required by source: + +- Stack defaults and Compose start from the 05:40-05:57 SAST best production + timing instead of the old broad cold-start range. +- The timing controller min/max bounds are pinned to that proven timing so it + cannot silently drift away without an explicit config and test change. +- Regression tests assert the timing defaults across `stack-defaults.env`, + `.env.example`, and Compose. diff --git a/docs/release-readiness-gates.html b/docs/release-readiness-gates.html index 0ec55c5..80219c2 100644 --- a/docs/release-readiness-gates.html +++ b/docs/release-readiness-gates.html @@ -34,7 +34,8 @@ "node mineable or synced state", "peer sanity with self and loopback peers filtered", "functional getBlockTemplate response", - "short mining RPC stability window" + "short mining RPC stability window", + "live node mutation guard for pool or paid-block evidence" ], "production_safety": "does not restart services or alter chain, pool, or database state" } @@ -61,8 +62,12 @@

Gates

Node RPCgetNodeInfo responds with a structured object.RPC auth, endpoint, module configuration, or node process is not ready. Mineable/syncedgetTemplateHealth reports mineable/submit-ready, or the older isCurrent fallback returns true.The node is reachable but not a safe mining backend yet. Peer sanityAt least one active sane peer remains after filtering the node itself, empty or unparseable addresses, loopback, unspecified, and inactive peers.The node may be isolated or accidentally connected only to itself/local addresses, or peer discovery may be advertising unusable peers. - Template functiongetBlockTemplate returns required mining fields including height, parent, roots, coinbase address, and nbits.The pool cannot create valid Stratum jobs even if containers look healthy. + Template functiongetBlockTemplate is called with the expected payout address and returns required mining fields including height, parent, roots, matching non-zero coinbase address, and nbits.The pool cannot create valid paid Stratum jobs even if containers look healthy, or it may waste accepted ASIC work on zero-address blocks. Mining RPC stabilityThe mineable/synced, peer sanity, and template checks keep passing across the configured short sample window.The node or RPC path may be flapping during startup, peer discovery, or IBD recovery. + Live node mutation guardStatus-sampler tests prove node config edits and node recreates are blocked while pool is running or any accepted-block history exists.A repair loop could restart a live mining node, lose the current datadir/identity view, and force the pool into catch-up instead of paid block production. + Repair-hold mining recoveryAutomation-control tests prove repair_hold may only allow explicitly allow-listed pool/ASIC recovery actions; node restarts, node recreates, and config edits remain blocked.A stale incident hold can prevent mining recovery after native safety is green, or an over-broad hold exception can mutate the node while it is production-active. + Native advisory-sync guardTests prove EVM/public-reference lag and stale catch-up state become advisory only when native mining proof is complete: current chain, fresh P2P, peer floor, peer lead, template readiness, and submit readiness.The pool may stay paused after catch-up or, worse, resume from public/EVM lag without enough native-chain proof. + Pool job readinessStatus tests prove connected miners with zero ready lanes keep can_submit_blocks=false unless fresh paid-block evidence proves the submit path is already working.The dashboard can report submit readiness while miners have no usable jobs. Deployment portabilityThe local RC validator passes from a clean temporary source copy and the dashboard collectors use Python HTTP rather than host-only tools.The release may work on the maintainer machine but fail on Pi5, Linux AMD64, macOS Docker Desktop, or Windows Docker Desktop because local runtime state or missing host utilities leaked into validation. @@ -90,6 +95,7 @@

Release Use

# For CI or an installer: ./scripts/release-readiness-check.py --json

Do not bypass a failed gate by marking the deployment healthy. Fix the failing dependency or wait for sync/template readiness, then rerun the checker.

+

Do not use catch-up, peer-list, cache, or template-support repair paths to recreate a node that is serving a live pool or has accepted-block history. Stop the mining path intentionally before datadir restore or node replacement work, then re-enter service only after native template health is safe.

diff --git a/ops/README.md b/ops/README.md index 1be1403..0ac1ae3 100644 --- a/ops/README.md +++ b/ops/README.md @@ -29,9 +29,9 @@ The dashboard also watches for pool share stalls. If miners are connected but th When the node is catching up, automation leaves the pool container running. The pool's node-health gate pauses `getBlockTemplate` refreshes while the node reports template generation is not ready, so miners are not disconnected just to reduce template pressure. -The watchdog also has a fast-sync recovery path. If real syncing warnings persist for `BDAG_WATCHDOG_SYNCING_THRESHOLD` checks, default `5`, it runs a normal stack restart to force fresh peer/RPC connections and apply the current config. This restart is cooldown-limited by `BDAG_SYNCING_RESTART_COOLDOWN`, default `900` seconds, so it cannot loop continuously. +The watchdog also has a fast-sync recovery path. If real syncing warnings persist for `BDAG_WATCHDOG_SYNCING_THRESHOLD` checks, default `5`, it runs a normal stack restart to force fresh peer/RPC connections and apply the current config. Native P2P peer loss is a hard mining block immediately, but the watchdog waits for `BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS`, default `300`, before restarting the node so a transient zero-peer sample cannot interrupt paid mining. Restarts are also cooldown-limited by `BDAG_SYNCING_RESTART_COOLDOWN`, default `900` seconds, so they cannot loop continuously. -The persisted peer list in `.env` should contain only valid multiaddrs. Removing a bad peer from `.env` takes effect on the next controlled node restart; it does not interrupt currently running miners by itself. +The persisted peer list in `.env` should contain only valid multiaddrs. Removing a bad peer from `.env` takes effect on the next controlled node restart; it does not interrupt currently running miners by itself. Chain-state self-heal requires active native P2P peer-lag evidence; EVM/public-reference lag alone is advisory and must not trigger destructive chain restore. The pool is configured to use the local node service directly as its DAG RPC endpoint on the next stack start. The dashboard compares the local chain view against external references where configured. diff --git a/ops/allow-p2p-iptables.sh b/ops/allow-p2p-iptables.sh index 234d0df..99c3d24 100755 --- a/ops/allow-p2p-iptables.sh +++ b/ops/allow-p2p-iptables.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh set -eu -PORT="${P2P_PORT:-8150}" +PORT="${P2P_PORT:-8155}" PROTOCOLS="${BDAG_P2P_PROTOCOLS:-tcp}" add_rule() { diff --git a/ops/automation_control.py b/ops/automation_control.py index 2eedff6..56f0809 100644 --- a/ops/automation_control.py +++ b/ops/automation_control.py @@ -77,6 +77,12 @@ } LOW_RISK_ACTIONS = {ACTION_READ_STATUS, ACTION_WRITE_INCIDENT} CONTAINMENT_ACTIONS = {ACTION_CONTAINMENT_STOP} +REPAIR_HOLD_RECOVERY_ACTIONS = { + ACTION_ASIC_MINER_OPEN_RESTART, + ACTION_ASIC_MINER_RESTART, + ACTION_ASIC_POOL_START, + ACTION_ASIC_POOL_RESTART, +} @dataclass(frozen=True) @@ -338,6 +344,12 @@ def _transition_hold_allows(control: dict[str, Any], action: str, actor: str, ta return any(item in tokens for item in allowed) +def _repair_hold_recovery_allows(control: dict[str, Any], action: str, actor: str, target: str) -> bool: + if action not in REPAIR_HOLD_RECOVERY_ACTIONS: + return False + return _transition_hold_allows(control, action, actor, target) + + def is_high_risk_action(action: str) -> bool: if action in LOW_RISK_ACTIONS or action in CONTAINMENT_ACTIONS: return False @@ -411,7 +423,27 @@ def check_mutation_allowed( maybe_log_denial_event(decision, requested_reason=reason, event_path=event_path, lock_path=lock_path) return decision - if state in BLOCKING_STATES and high_risk: + if state == STATE_REPAIR_HOLD and high_risk: + allowed = _repair_hold_recovery_allows(control, action, actor, target) + decision = ControlDecision( + allowed, + action, + actor, + target, + state, + status, + ( + "repair_hold mining recovery allow-list matched" + if allowed + else f"automation control state {state} denies high-risk mutation" + ), + str(path), + ) + if not allowed and log_denial: + maybe_log_denial_event(decision, requested_reason=reason, event_path=event_path, lock_path=lock_path) + return decision + + if state in {STATE_CONTROLLED_STOP, STATE_CHAIN_INCIDENT} and high_risk: decision = ControlDecision( False, action, diff --git a/ops/chain-state-self-heal.sh b/ops/chain-state-self-heal.sh old mode 100644 new mode 100755 index 6486c22..7f65628 --- a/ops/chain-state-self-heal.sh +++ b/ops/chain-state-self-heal.sh @@ -191,6 +191,20 @@ if not isinstance(payload, dict): sync = payload.get("sync_health") if isinstance(payload.get("sync_health"), dict) else {} nodes = payload.get("nodes") if isinstance(payload.get("nodes"), dict) else {} chain_state = bool(sync.get("needs_chain_data_restore") or sync.get("chain_state_blocker")) +evm_watch = sync.get("evm_reference_gap_watch") if isinstance(sync.get("evm_reference_gap_watch"), dict) else {} +soft_evm_restore_only = bool(sync.get("evm_reference_gap_stalled") or evm_watch.get("restore_required")) and not bool( + sync.get("chain_state_blocker") + or sync.get("chain_data_restore_required") + or sync.get("chain_state_blocker_nodes") + or sync.get("chain_data_restore_nodes") +) +native_paid_safe = bool( + sync.get("native_progress_paid_work_safe") + or (sync.get("selected_backend_mining_safe") and sync.get("pool_has_recent_paid_work")) +) +if chain_state and soft_evm_restore_only and native_paid_safe: + print("EVM reference gap is advisory under native-safe paid mining") + sys.exit(1) if not chain_state: chain_state = any(isinstance(info, dict) and info.get("chain_state_blocker") for info in nodes.values()) if chain_state: diff --git a/ops/compose_migrations.py b/ops/compose_migrations.py index df1ab00..8af060f 100644 --- a/ops/compose_migrations.py +++ b/ops/compose_migrations.py @@ -10,6 +10,7 @@ ("POOL_SUBMIT_STALE_BLOCK_CANDIDATES", "${POOL_SUBMIT_STALE_BLOCK_CANDIDATES:-false}"), ("POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED", "${POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED:-true}"), ("POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD", "${POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD:-1}"), + ("POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD", "${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0}"), ("POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD", "${POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD:-3}"), ("POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS", "${POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS:-120}"), ("POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS", "${POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS:-60}"), diff --git a/ops/config/stack-defaults.env b/ops/config/stack-defaults.env index a511018..5b35949 100644 --- a/ops/config/stack-defaults.env +++ b/ops/config/stack-defaults.env @@ -17,10 +17,13 @@ BDAG_NODE_SERVICES=node BDAG_STACK_SERVICES=postgres,node,pool # Node and sync defaults. +NODE_DATA_DIR=./data/node SYNC_SOURCE_NODE=0 BDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1 BDAG_CHAIN_PEERSTORE_LOG_TAIL=8000 BDAG_NO_FASTSYNC_SERVE=auto +BDAG_NODE_DEBUG_LEVEL=warn +BDAG_NODE_NO_FILE_LOGGING=1 BDAG_FASTARTIFACTSYNC_ENABLED=1 BDAG_FASTSYNC_RANGE_BLOCKS=1024 BDAG_FASTSYNC_PREPROCESS_WORKERS=1 @@ -85,7 +88,8 @@ BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60 BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS=60 BDAG_DASHBOARD_GLOBAL_SAMPLER_INTERVAL_SECONDS=60 BDAG_DOCKER_SOCKET_GID=0 -BDAG_GLOBAL_RPC_WORKERS=24 +BDAG_EVM_SYNC_BACKOFF_SECONDS=60 +BDAG_GLOBAL_RPC_WORKERS=4 BDAG_GLOBAL_BLOCK_WINDOW=600 BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30 BDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=2 @@ -135,23 +139,44 @@ BDAG_BACKGROUND_MAINTENANCE_POOL_READY_TASKS=rawdatadir_publish,ipfs_content_sid POOL_GBT_MIN_INTERVAL_MS=1100 POOL_GBT_PRESSURE_INTERVAL_MS=500 POOL_GBT_PRESSURE_WINDOW_SECONDS=10 +POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5 +POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60 +POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp +POOL_ASIC_MAC_ALLOWLIST= +POOL_ASIC_MAC_OVERRIDES= POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15 POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30 -POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=true +POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15 +POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2 +POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30 POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false -POOL_TEMPLATE_TTL_REFRESH_MS=2000 -POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=2500 +POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750 +POOL_TEMPLATE_TTL_REFRESH_MS=100 +POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750 +POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100 +POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100 +POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true +POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000 POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1 +POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0 POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5 POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3 POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120 POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60 POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true +POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0 +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20 +POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000 +BDAG_ENABLE_NODE_MINING=0 +BDAG_NODE_MODULES=Blockdag,miner # Low-priority rawdatadir/IPFS sidecar. BDAG_RAWDATADIR_SOURCE_MODE=auto diff --git a/ops/deploy-live-runtime-update.sh b/ops/deploy-live-runtime-update.sh index 675434b..f3a97ce 100755 --- a/ops/deploy-live-runtime-update.sh +++ b/ops/deploy-live-runtime-update.sh @@ -310,7 +310,8 @@ migrate_runtime_compose() { for key in \ POOL_SUBMIT_STALE_BLOCK_CANDIDATES \ POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED \ - POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD + POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD \ + POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD do if ! grep -q "${key}:" "$compose"; then missing=1 diff --git a/ops/install-dashboard.sh b/ops/install-dashboard.sh index 314981a..846df05 100755 --- a/ops/install-dashboard.sh +++ b/ops/install-dashboard.sh @@ -221,6 +221,7 @@ BDAG_DASHBOARD_DIRECT_STATUS_FALLBACK=$(stack_default BDAG_DASHBOARD_DIRECT_STAT BDAG_DASHBOARD_STATUS_CACHE_SECONDS=$(stack_default BDAG_DASHBOARD_STATUS_CACHE_SECONDS) BDAG_DASHBOARD_SAMPLER_CACHE_SECONDS=$(stack_default BDAG_DASHBOARD_SAMPLER_CACHE_SECONDS) BDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS=$(stack_default BDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS) +BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS=$(stack_default BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS) BDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=$(stack_default BDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS) BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=$(stack_default BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS) BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS=$(stack_default BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS) @@ -387,6 +388,7 @@ ensure_stack_default_env_value BDAG_DASHBOARD_DIRECT_STATUS_FALLBACK ensure_stack_default_env_value BDAG_DASHBOARD_STATUS_CACHE_SECONDS ensure_stack_default_env_value BDAG_DASHBOARD_SAMPLER_CACHE_SECONDS ensure_stack_default_env_value BDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS +ensure_stack_default_env_value BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS ensure_stack_default_env_value BDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS ensure_stack_default_env_value BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS ensure_stack_default_env_value BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS diff --git a/ops/install-p2p-services.sh b/ops/install-p2p-services.sh index af34392..f97bd05 100755 --- a/ops/install-p2p-services.sh +++ b/ops/install-p2p-services.sh @@ -2,7 +2,7 @@ set -euo pipefail ROOT="${BDAG_PROJECT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" -P2P_PORT="${P2P_PORT:-8150}" +P2P_PORT="${P2P_PORT:-8155}" P2P_PROTOCOLS="${BDAG_P2P_PROTOCOLS:-tcp}" warn() { printf 'WARNING: %s\n' "$*" >&2; } diff --git a/ops/mining_readiness_gate.py b/ops/mining_readiness_gate.py index b2bfbe7..e24a72c 100644 --- a/ops/mining_readiness_gate.py +++ b/ops/mining_readiness_gate.py @@ -24,6 +24,12 @@ DEFAULT_SAMPLE_COUNT = 3 DEFAULT_SAMPLE_INTERVAL_SECONDS = 10.0 DEFAULT_MAX_REFERENCE_LAG = 120 +DEFAULT_STRICT_REFERENCE_LAG = os.environ.get("BDAG_STRICT_REFERENCE_LAG", "").strip().lower() in { + "1", + "true", + "yes", + "on", +} DEFAULT_LOG_LOOKBACK_MINUTES = 15 DEFAULT_POW_TYPE = 10 JSON_RPC_CONTENT_TYPE = "application/json" @@ -454,6 +460,7 @@ def probe_backend_once( timeout: float = DEFAULT_TIMEOUT_SECONDS, after_chain_incident: bool = False, max_reference_lag: int = DEFAULT_MAX_REFERENCE_LAG, + strict_reference_lag: bool = DEFAULT_STRICT_REFERENCE_LAG, allow_reference_unavailable: bool = False, pow_type: int = DEFAULT_POW_TYPE, mining_address: str = "", @@ -553,7 +560,7 @@ def probe_backend_once( warnings, health, "p2p_mining_fresh", - required=False, + required=True, required_after_incident=True, after_chain_incident=after_chain_incident, ) @@ -613,13 +620,21 @@ def probe_backend_once( if height is not None and reference_height is not None: lag = int(reference_height) - int(height) if lag > max_reference_lag: - failures.append(f"reference_height_lag_{lag}_gt_{max_reference_lag}") + reason = f"reference_height_lag_{lag}_gt_{max_reference_lag}" + if strict_reference_lag: + failures.append(reason) + else: + warnings.append(reason) reference_main_order = reference.get("main_order") main_order = sample.get("main_order") if main_order is not None and reference_main_order is not None: lag = int(reference_main_order) - int(main_order) if lag > max_reference_lag: - failures.append(f"reference_main_order_lag_{lag}_gt_{max_reference_lag}") + reason = f"reference_main_order_lag_{lag}_gt_{max_reference_lag}" + if strict_reference_lag: + failures.append(reason) + else: + warnings.append(reason) if not failures: sample["ok"] = True @@ -638,6 +653,7 @@ def evaluate_backend( sample_interval_seconds: float = DEFAULT_SAMPLE_INTERVAL_SECONDS, after_chain_incident: bool = False, max_reference_lag: int = DEFAULT_MAX_REFERENCE_LAG, + strict_reference_lag: bool = DEFAULT_STRICT_REFERENCE_LAG, allow_reference_unavailable: bool = False, pow_type: int = DEFAULT_POW_TYPE, mining_address: str = "", @@ -668,6 +684,7 @@ def evaluate_backend( timeout=timeout, after_chain_incident=after_chain_incident, max_reference_lag=max_reference_lag, + strict_reference_lag=strict_reference_lag, allow_reference_unavailable=allow_reference_unavailable, pow_type=pow_type, mining_address=mining_address, @@ -839,6 +856,7 @@ def evaluate_gate( sample_interval_seconds: float = DEFAULT_SAMPLE_INTERVAL_SECONDS, after_chain_incident: bool = False, max_reference_lag: int = DEFAULT_MAX_REFERENCE_LAG, + strict_reference_lag: bool = DEFAULT_STRICT_REFERENCE_LAG, allow_reference_unavailable: bool = False, log_sources: list[str] | None = None, log_lookback_minutes: int = DEFAULT_LOG_LOOKBACK_MINUTES, @@ -860,6 +878,7 @@ def evaluate_gate( sample_interval_seconds=sample_interval_seconds, after_chain_incident=after_chain_incident, max_reference_lag=max_reference_lag, + strict_reference_lag=strict_reference_lag, allow_reference_unavailable=allow_reference_unavailable, pow_type=pow_type, mining_address=resolved_mining_address, @@ -898,6 +917,7 @@ def evaluate_gate( "timeout_seconds": timeout, "after_chain_incident": after_chain_incident, "max_reference_lag": max_reference_lag, + "strict_reference_lag": strict_reference_lag, "allow_reference_unavailable": allow_reference_unavailable, "pow_type": pow_type, "mining_address_present": bool(resolved_mining_address), @@ -917,6 +937,12 @@ def build_parser() -> argparse.ArgumentParser: parser.add_argument("--samples", type=int, default=DEFAULT_SAMPLE_COUNT) parser.add_argument("--sample-interval", type=float, default=DEFAULT_SAMPLE_INTERVAL_SECONDS) parser.add_argument("--max-reference-lag", type=int, default=DEFAULT_MAX_REFERENCE_LAG) + parser.add_argument( + "--strict-reference-lag", + action="store_true", + default=DEFAULT_STRICT_REFERENCE_LAG, + help="Fail readiness on public reference lag instead of recording it as advisory", + ) parser.add_argument("--pow-type", type=int, default=DEFAULT_POW_TYPE) parser.add_argument("--mining-address", default=None) parser.add_argument( @@ -951,6 +977,7 @@ def main(argv: list[str] | None = None) -> int: sample_interval_seconds=args.sample_interval, after_chain_incident=args.after_chain_incident, max_reference_lag=args.max_reference_lag, + strict_reference_lag=args.strict_reference_lag, allow_reference_unavailable=args.allow_reference_unavailable, log_sources=args.log_source, log_lookback_minutes=args.log_lookback_minutes, diff --git a/ops/optimization_measurement.py b/ops/optimization_measurement.py index a245d18..f51c4b7 100644 --- a/ops/optimization_measurement.py +++ b/ops/optimization_measurement.py @@ -5,6 +5,7 @@ import argparse import json +import re import time import urllib.request from pathlib import Path @@ -12,6 +13,11 @@ from pool_ops import RUNTIME_DIR, collect_status_cached, host_runtime_profile, now_iso, seconds_since_epoch +DEFAULT_POOL_METRICS_URL = "http://127.0.0.1:9090/metrics" +DEFAULT_LOCAL_STATUS_MAX_AGE_SECONDS = 5.0 +METRIC_RE = re.compile(r"^([a-zA-Z_:][a-zA-Z0-9_:]*)(?:\{([^}]*)\})?\s+([-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)$") +LABEL_RE = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)="((?:[^"\\]|\\.)*)"') + def percentile(values: list[float], pct: float) -> float | None: if not values: @@ -37,17 +43,169 @@ def fetch_status_url(url: str, timeout: float) -> tuple[dict[str, Any], float]: return payload if isinstance(payload, dict) else {}, round((time.monotonic() - started) * 1000, 3) -def collect_status_sample(status_url: str | None = None, timeout: float = 8.0) -> dict[str, Any]: +def fetch_text_url(url: str, timeout: float) -> tuple[str, float]: + started = time.monotonic() + with urllib.request.urlopen(url, timeout=timeout) as response: + return response.read().decode("utf-8", "replace"), round((time.monotonic() - started) * 1000, 3) + + +def parse_prometheus_metrics(text: str) -> dict[tuple[str, tuple[tuple[str, str], ...]], float]: + metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float] = {} + for raw in text.splitlines(): + line = raw.strip() + if not line or line.startswith("#"): + continue + match = METRIC_RE.match(line) + if not match: + continue + name, raw_labels, raw_value = match.groups() + labels = tuple(sorted((item.group(1), bytes(item.group(2), "utf-8").decode("unicode_escape")) for item in LABEL_RE.finditer(raw_labels or ""))) + metrics[(name, labels)] = float(raw_value) + return metrics + + +def metric_labels_match(labels: tuple[tuple[str, str], ...], wanted: dict[str, str]) -> bool: + found = dict(labels) + return all(found.get(key) == value for key, value in wanted.items()) + + +def metric_sum(metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], name: str, labels: dict[str, str] | None = None) -> float | None: + values = [ + value + for (metric_name, metric_labels), value in metrics.items() + if metric_name == name and (not labels or metric_labels_match(metric_labels, labels)) + ] + if not values: + return None + return round(sum(values), 6) + + +def metric_sum_by_label( + metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], + name: str, + group_label: str, + labels: dict[str, str] | None = None, + exclude_labels: dict[str, str] | None = None, +) -> dict[str, float]: + grouped: dict[str, float] = {} + for (metric_name, metric_labels), value in metrics.items(): + if metric_name != name: + continue + label_map = dict(metric_labels) + if labels and not all(label_map.get(key) == wanted for key, wanted in labels.items()): + continue + if exclude_labels and any(label_map.get(key) == unwanted for key, unwanted in exclude_labels.items()): + continue + key = label_map.get(group_label) or "unlabeled" + grouped[key] = round(grouped.get(key, 0.0) + value, 6) + return dict(sorted(grouped.items())) + + +def metric_max(metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], name: str) -> float | None: + values = [value for (metric_name, _), value in metrics.items() if metric_name == name] + if not values: + return None + return round(max(values), 6) + + +def flatten_pool_metrics(metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], latency_ms: float | None = None, error: str | None = None) -> dict[str, Any]: + block_rejected = 0.0 + block_rejected_seen = False + for (name, labels), value in metrics.items(): + if name != "pool_block_submit_outcomes_total": + continue + label_map = dict(labels) + if label_map.get("outcome") != "accepted": + block_rejected += value + block_rejected_seen = True + + return { + "pool_metrics_latency_ms": latency_ms, + "pool_metrics_error": error, + "pool_active_connections": metric_sum(metrics, "pool_active_connections"), + "pool_job_health_ok": metric_max(metrics, "pool_job_health_ok"), + "pool_job_health_authorized_miners": metric_sum(metrics, "pool_job_health_authorized_miners"), + "pool_job_health_ready_miners": metric_sum(metrics, "pool_job_health_ready_miners"), + "pool_blocks_found_total": metric_sum(metrics, "pool_blocks_found_total"), + "pool_block_submit_accepted_total": metric_sum(metrics, "pool_block_submit_outcomes_total", {"outcome": "accepted"}), + "pool_block_submit_rejected_total": round(block_rejected, 6) if block_rejected_seen else None, + "pool_block_submit_rejected_by_reason": metric_sum_by_label( + metrics, + "pool_block_submit_outcomes_total", + "reason", + exclude_labels={"outcome": "accepted"}, + ), + "pool_shares_accepted_total": metric_sum(metrics, "pool_shares_accepted_total"), + "pool_shares_rejected_total": metric_sum(metrics, "pool_shares_rejected_total"), + "pool_share_reject_by_reason": metric_sum_by_label(metrics, "pool_shares_rejected_total", "reason"), + "pool_backend_mineable": metric_max(metrics, "pool_rpc_backend_node_health_mineable"), + "pool_backend_submit_ready": metric_max(metrics, "pool_rpc_backend_node_health_submit_ready"), + "pool_backend_p2p_mining_fresh": metric_max(metrics, "pool_rpc_backend_node_health_p2p_mining_fresh"), + "pool_backend_p2p_consensus_peer_count": metric_max(metrics, "pool_rpc_backend_node_health_p2p_consensus_peer_count"), + "pool_backend_p2p_fresh_consensus_peer_count": metric_max(metrics, "pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count"), + "pool_backend_p2p_best_peer_lead_blocks": metric_max(metrics, "pool_rpc_backend_node_health_p2p_best_peer_lead_blocks"), + "pool_backend_p2p_best_peer_graph_state_age_seconds": metric_max(metrics, "pool_rpc_backend_node_health_p2p_best_peer_graph_state_age_seconds"), + "pool_template_conversion_failure_ratio": metric_max(metrics, "pool_template_conversion_stall_failure_ratio"), + "pool_template_conversion_window_total": metric_sum(metrics, "pool_template_conversion_stall_window_candidates", {"kind": "total"}), + "pool_template_conversion_window_failed": metric_sum(metrics, "pool_template_conversion_stall_window_candidates", {"kind": "failed"}), + "pool_template_conversion_window_accepted": metric_sum(metrics, "pool_template_conversion_stall_window_candidates", {"kind": "accepted"}), + "pool_template_invalidation_by_cause": metric_sum_by_label( + metrics, + "pool_rpc_backend_node_health_template_invalidations_total", + "cause", + ), + "pool_clean_refresh_by_event": metric_sum_by_label(metrics, "pool_clean_template_refresh_events_total", "event"), + } + + +def collect_pool_metrics_sample(metrics_url: str | None, timeout: float) -> dict[str, Any]: + if not metrics_url: + return {} + try: + text, latency_ms = fetch_text_url(metrics_url, min(timeout, 3.0)) + return flatten_pool_metrics(parse_prometheus_metrics(text), latency_ms=latency_ms) + except Exception as exc: # noqa: BLE001 - measurement must not fail when metrics are absent. + return flatten_pool_metrics({}, error=str(exc)) + + +def collect_status_sample( + status_url: str | None = None, + timeout: float = 8.0, + pool_metrics_url: str | None = DEFAULT_POOL_METRICS_URL, + local_status_max_age_seconds: float = DEFAULT_LOCAL_STATUS_MAX_AGE_SECONDS, +) -> dict[str, Any]: started = time.monotonic() dashboard_latency_ms = None if status_url: status, dashboard_latency_ms = fetch_status_url(status_url, timeout) source = status_url else: - status = collect_status_cached(include_logs=False) + status = collect_status_cached(include_logs=False, max_age_seconds=local_status_max_age_seconds) source = "local-collector" collection_ms = round((time.monotonic() - started) * 1000, 3) - return flatten_status_sample(status, source, collection_ms, dashboard_latency_ms) + sample = flatten_status_sample(status, source, collection_ms, dashboard_latency_ms) + sample.update(collect_pool_metrics_sample(pool_metrics_url, timeout)) + return sample + + +def sync_current_block_source(sync: dict[str, Any], nodes: dict[str, Any], current_block: float | None) -> str | None: + source = sync.get("current_block_source") + if source: + return str(source) + chain_block_count = number(sync.get("chain_block_count")) + if current_block is not None and chain_block_count is not None and int(current_block) == int(chain_block_count): + return str(sync.get("source") or "native-chain-block-count") + node_sources: set[str] = set() + for node in nodes.values(): + if not isinstance(node, dict): + continue + node_current = number(node.get("current_block")) + node_source = node.get("current_block_source") or node.get("chain_rpc_source") + if current_block is not None and node_current is not None and int(current_block) == int(node_current) and node_source: + node_sources.add(str(node_source)) + if len(node_sources) == 1: + return next(iter(node_sources)) + return None def flatten_status_sample( @@ -60,6 +218,7 @@ def flatten_status_sample( host = status.get("host_pressure") if isinstance(status.get("host_pressure"), dict) else {} adaptive = status.get("adaptive_concurrency") if isinstance(status.get("adaptive_concurrency"), dict) else {} miner = status.get("miner_health") if isinstance(status.get("miner_health"), dict) else {} + sampler = status.get("status_sampler") if isinstance(status.get("status_sampler"), dict) else {} nodes = sync.get("nodes") if isinstance(sync.get("nodes"), dict) else {} chain_latencies = [ value @@ -68,6 +227,9 @@ def flatten_status_sample( ] current_block = number(sync.get("current_block")) highest_block = number(sync.get("highest_block")) + p2p_connections = number(sync.get("p2p_connections")) + p2p_network_gap = number(sync.get("p2p_network_gap")) + current_block_source = sync_current_block_source(sync, nodes, current_block) adaptive_workers = adaptive.get("workers") if isinstance(adaptive.get("workers"), dict) else {} return { "sampled_at": now_iso(), @@ -75,13 +237,25 @@ def flatten_status_sample( "source": source, "collection_ms": collection_ms, "dashboard_latency_ms": dashboard_latency_ms, + "status_age_seconds": number(status.get("age_seconds")), + "status_fresh": status.get("fresh"), + "status_sampler_hit": sampler.get("hit"), + "status_sampler_age_seconds": number(sampler.get("age_seconds")), "overall": status.get("overall"), "mode": status.get("mode"), "can_mine": status.get("can_mine"), + "can_accept_shares": status.get("can_accept_shares"), + "can_submit_blocks": status.get("can_submit_blocks"), "sync_status": sync.get("status"), "current_block": int(current_block) if current_block is not None else None, "highest_block": int(highest_block) if highest_block is not None else None, + "current_block_source": current_block_source, "remaining_blocks": int(number(sync.get("remaining_blocks")) or 0) if sync.get("remaining_blocks") is not None else None, + "native_is_current": sync.get("native_is_current"), + "chain_syncing": sync.get("chain_syncing"), + "mining_advisory_sync": sync.get("mining_advisory_sync"), + "p2p_connections": int(p2p_connections) if p2p_connections is not None else None, + "p2p_network_gap": int(p2p_network_gap) if p2p_network_gap is not None else None, "chain_rpc_latency_ms_max": max(chain_latencies) if chain_latencies else None, "chain_rpc_latency_ms_avg": round(sum(chain_latencies) / len(chain_latencies), 3) if chain_latencies else None, "connected_miners": int(number(miner.get("connected_count")) or 0), @@ -110,16 +284,62 @@ def summarize_samples(samples: list[dict[str, Any]]) -> dict[str, Any]: elapsed = max(0, float(last.get("sampled_epoch") or 0) - float(first.get("sampled_epoch") or 0)) first_block = number(first.get("current_block")) last_block = number(last.get("current_block")) + first_block_source = str(first.get("current_block_source") or "") + last_block_source = str(last.get("current_block_source") or "") + block_source_missing = not first_block_source or not last_block_source + block_source_changed = bool(first_block_source and last_block_source and first_block_source != last_block_source) block_delta = None blocks_per_second = None + block_delta_valid = False if first_block is not None and last_block is not None: - block_delta = int(last_block - first_block) - if elapsed > 0: + raw_block_delta = int(last_block - first_block) + block_delta_valid = bool(raw_block_delta >= 0 and not block_source_missing and not block_source_changed) + if block_delta_valid: + block_delta = raw_block_delta + if elapsed > 0 and block_delta is not None: blocks_per_second = round(block_delta / elapsed, 4) def values(field: str) -> list[float]: return [value for value in (number(sample.get(field)) for sample in samples) if value is not None] + def counter_delta(field: str, *, missing_initial_zero: bool = False) -> float | None: + first_value = number(first.get(field)) + last_value = number(last.get(field)) + if first_value is None and missing_initial_zero and last_value is not None and not first.get("pool_metrics_error"): + first_value = 0.0 + if first_value is None or last_value is None: + return None + if last_value < first_value: + return round(last_value, 6) + return round(last_value - first_value, 6) + + def counter_delta_map(field: str) -> dict[str, float]: + first_values = first.get(field) if isinstance(first.get(field), dict) else {} + last_values = last.get(field) if isinstance(last.get(field), dict) else {} + deltas: dict[str, float] = {} + for key in sorted(set(first_values) | set(last_values)): + first_value = number(first_values.get(key)) or 0.0 + last_value = number(last_values.get(key)) or 0.0 + delta = last_value if last_value < first_value else last_value - first_value + if delta != 0: + deltas[str(key)] = round(delta, 6) + return deltas + + def bool_values(field: str) -> list[str]: + found: set[str] = set() + for sample in samples: + value = sample.get(field) + if isinstance(value, bool): + found.add("true" if value else "false") + elif value is not None: + found.add(str(value)) + return sorted(found) + + accepted_delta = counter_delta("pool_block_submit_accepted_total") + rejected_delta = counter_delta("pool_block_submit_rejected_total", missing_initial_zero=True) + shares_accepted_delta = counter_delta("pool_shares_accepted_total") + shares_rejected_delta = counter_delta("pool_shares_rejected_total", missing_initial_zero=True) + worker_ranges: dict[str, dict[str, int]] = {} for sample in samples: workers = sample.get("adaptive_workers") if isinstance(sample.get("adaptive_workers"), dict) else {} @@ -144,14 +364,54 @@ def values(field: str) -> list[float]: "mode_values": sorted({str(sample.get("mode")) for sample in samples if sample.get("mode")}), "sync_status_values": sorted({str(sample.get("sync_status")) for sample in samples if sample.get("sync_status")}), "block_delta": block_delta, + "block_delta_valid": block_delta_valid, + "block_delta_warning": "current_block source missing, changed, or moved backwards" + if not block_delta_valid and first_block is not None and last_block is not None + else "", "blocks_per_second": blocks_per_second, "current_block_first": first.get("current_block"), "current_block_last": last.get("current_block"), + "current_block_source_first": first.get("current_block_source"), + "current_block_source_last": last.get("current_block_source"), "remaining_blocks_last": last.get("remaining_blocks"), + "can_mine_values": bool_values("can_mine"), + "can_submit_blocks_values": bool_values("can_submit_blocks"), + "native_is_current_values": bool_values("native_is_current"), + "chain_syncing_values": bool_values("chain_syncing"), + "mining_advisory_sync_values": bool_values("mining_advisory_sync"), + "p2p_connections_min": min(values("p2p_connections") or [0]), + "p2p_network_gap_max": percentile(values("p2p_network_gap"), 100), "connected_miners_max": max(values("connected_miners") or [0]), "managed_miners_max": max(values("managed_miners") or [0]), + "pool_active_connections_max": max(values("pool_active_connections") or [0]), + "pool_ready_miners_min": min(values("pool_job_health_ready_miners") or [0]), + "pool_ready_miners_max": max(values("pool_job_health_ready_miners") or [0]), + "pool_backend_mineable_min": min(values("pool_backend_mineable") or [0]), + "pool_backend_submit_ready_min": min(values("pool_backend_submit_ready") or [0]), + "pool_backend_p2p_fresh_min": min(values("pool_backend_p2p_mining_fresh") or [0]), + "pool_backend_peer_lead_max": percentile(values("pool_backend_p2p_best_peer_lead_blocks"), 100), + "pool_fresh_consensus_peers_min": min(values("pool_backend_p2p_fresh_consensus_peer_count") or [0]), + "pool_block_submit_accepted_delta": accepted_delta, + "pool_block_submit_rejected_delta": rejected_delta, + "pool_block_submit_rejected_per_accepted": round(rejected_delta / accepted_delta, 6) if accepted_delta and rejected_delta is not None else None, + "pool_block_submit_rejected_by_reason_delta": counter_delta_map("pool_block_submit_rejected_by_reason"), + "pool_accepted_blocks_per_hour": round(accepted_delta * 3600.0 / elapsed, 3) if accepted_delta is not None and elapsed > 0 else None, + "pool_blocks_found_delta": counter_delta("pool_blocks_found_total"), + "pool_shares_accepted_delta": shares_accepted_delta, + "pool_shares_rejected_delta": shares_rejected_delta, + "pool_share_reject_ratio": round(shares_rejected_delta / max(1.0, shares_accepted_delta + shares_rejected_delta), 6) + if shares_accepted_delta is not None and shares_rejected_delta is not None + else None, + "pool_share_reject_by_reason_delta": counter_delta_map("pool_share_reject_by_reason"), + "pool_template_invalidation_by_cause_delta": counter_delta_map("pool_template_invalidation_by_cause"), + "pool_clean_refresh_by_event_delta": counter_delta_map("pool_clean_refresh_by_event"), + "pool_template_conversion_failure_ratio_max": percentile(values("pool_template_conversion_failure_ratio"), 100), "collection_ms_p95": percentile(values("collection_ms"), 95), "dashboard_latency_ms_p95": percentile(values("dashboard_latency_ms"), 95), + "status_age_seconds_p95": percentile(values("status_age_seconds"), 95), + "status_age_seconds_max": percentile(values("status_age_seconds"), 100), + "status_sampler_hit_values": bool_values("status_sampler_hit"), + "pool_metrics_latency_ms_p95": percentile(values("pool_metrics_latency_ms"), 95), "chain_rpc_latency_ms_p95": percentile(values("chain_rpc_latency_ms_max"), 95), "iowait_percent_max": percentile(values("iowait_percent"), 100), "io_some_avg10_max": percentile(values("io_some_avg10"), 100), @@ -177,10 +437,41 @@ def render_html_report(summary: dict[str, Any], samples: list[dict[str, Any]]) - ("Source", summary.get("source")), ("Modes", ", ".join(summary.get("mode_values") or [])), ("Sync statuses", ", ".join(summary.get("sync_status_values") or [])), + ("Can submit values", ", ".join(summary.get("can_submit_blocks_values") or [])), + ("Native current values", ", ".join(summary.get("native_is_current_values") or [])), + ("Mining advisory sync values", ", ".join(summary.get("mining_advisory_sync_values") or [])), ("Block delta", summary.get("block_delta")), + ("Block delta valid", summary.get("block_delta_valid")), + ("Block delta warning", summary.get("block_delta_warning")), + ("Current block source first/last", f"{summary.get('current_block_source_first')} / {summary.get('current_block_source_last')}"), ("Blocks/sec", summary.get("blocks_per_second")), + ("Pool accepted blocks delta", summary.get("pool_block_submit_accepted_delta")), + ("Pool rejected blocks delta", summary.get("pool_block_submit_rejected_delta")), + ("Pool rejected / accepted", summary.get("pool_block_submit_rejected_per_accepted")), + ("Pool rejected reasons", json.dumps(summary.get("pool_block_submit_rejected_by_reason_delta") or {}, sort_keys=True)), + ("Pool accepted blocks / hour", summary.get("pool_accepted_blocks_per_hour")), + ("Pool found blocks delta", summary.get("pool_blocks_found_delta")), + ("Pool accepted shares delta", summary.get("pool_shares_accepted_delta")), + ("Pool rejected shares delta", summary.get("pool_shares_rejected_delta")), + ("Pool share reject ratio", summary.get("pool_share_reject_ratio")), + ("Pool share reject reasons", json.dumps(summary.get("pool_share_reject_by_reason_delta") or {}, sort_keys=True)), + ("Pool active connections max", summary.get("pool_active_connections_max")), + ("Pool ready miners min/max", f"{summary.get('pool_ready_miners_min')} / {summary.get('pool_ready_miners_max')}"), + ("Pool backend mineable min", summary.get("pool_backend_mineable_min")), + ("Pool backend submit-ready min", summary.get("pool_backend_submit_ready_min")), + ("Pool P2P fresh min", summary.get("pool_backend_p2p_fresh_min")), + ("Pool backend peer lead max", summary.get("pool_backend_peer_lead_max")), + ("P2P connections min", summary.get("p2p_connections_min")), + ("P2P network gap max", summary.get("p2p_network_gap_max")), + ("Pool fresh consensus peers min", summary.get("pool_fresh_consensus_peers_min")), + ("Template invalidation causes", json.dumps(summary.get("pool_template_invalidation_by_cause_delta") or {}, sort_keys=True)), + ("Clean refresh events", json.dumps(summary.get("pool_clean_refresh_by_event_delta") or {}, sort_keys=True)), + ("Pool conversion failure max %", summary.get("pool_template_conversion_failure_ratio_max")), ("Collection p95 ms", summary.get("collection_ms_p95")), ("Dashboard p95 ms", summary.get("dashboard_latency_ms_p95")), + ("Status age p95 / max s", f"{summary.get('status_age_seconds_p95')} / {summary.get('status_age_seconds_max')}"), + ("Status sampler hit values", ", ".join(summary.get("status_sampler_hit_values") or [])), + ("Pool metrics p95 ms", summary.get("pool_metrics_latency_ms_p95")), ("Chain RPC p95 ms", summary.get("chain_rpc_latency_ms_p95")), ("I/O wait max %", summary.get("iowait_percent_max")), ("IO PSI avg10 max", summary.get("io_some_avg10_max")), @@ -201,10 +492,19 @@ def render_html_report(summary: dict[str, Any], samples: list[dict[str, Any]]) - f"{html_escape(sample.get('overall'))}" f"{html_escape(sample.get('mode'))}" f"{html_escape(sample.get('sync_status'))}" + f"{html_escape(sample.get('can_submit_blocks'))}" + f"{html_escape(sample.get('native_is_current'))}" + f"{html_escape(sample.get('mining_advisory_sync'))}" + f"{html_escape(sample.get('status_age_seconds'))}" f"{html_escape(sample.get('current_block'))}" + f"{html_escape(sample.get('current_block_source'))}" f"{html_escape(sample.get('remaining_blocks'))}" f"{html_escape(sample.get('chain_rpc_latency_ms_max'))}" f"{html_escape(sample.get('iowait_percent'))}" + f"{html_escape(sample.get('pool_block_submit_accepted_total'))}" + f"{html_escape(sample.get('pool_job_health_ready_miners'))}" + f"{html_escape(sample.get('pool_backend_submit_ready'))}" + f"{html_escape(sample.get('pool_backend_p2p_mining_fresh'))}" "" for sample in last_samples ) @@ -226,7 +526,7 @@ def render_html_report(summary: dict[str, Any], samples: list[dict[str, Any]]) -

Adaptive Worker Ranges

{worker_rows}
KindMinMax

Recent Samples

- {sample_rows}
TimeOverallModeSyncBlockRemainingRPC msIO wait %
+ {sample_rows}
TimeOverallModeSyncCan SubmitNative CurrentAdvisory SyncStatus Age sBlockBlock SourceRemainingRPC msIO wait %Accepted BlocksReady MinersSubmit ReadyP2P Fresh
""" @@ -241,7 +541,12 @@ def run_measurement(args: argparse.Namespace) -> dict[str, Any]: samples: list[dict[str, Any]] = [] deadline = time.monotonic() + max(0.0, args.duration_seconds) while True: - sample = collect_status_sample(args.status_url, timeout=args.timeout_seconds) + sample = collect_status_sample( + args.status_url, + timeout=args.timeout_seconds, + pool_metrics_url=args.pool_metrics_url, + local_status_max_age_seconds=args.local_status_max_age_seconds, + ) samples.append(sample) with jsonl_path.open("a", encoding="utf-8") as handle: handle.write(json.dumps(sample, sort_keys=True) + "\n") @@ -269,10 +574,20 @@ def main() -> int: parser.add_argument("--interval-seconds", type=float, default=10.0) parser.add_argument("--timeout-seconds", type=float, default=8.0) parser.add_argument("--status-url", help="optional dashboard /api/status URL to measure HTTP latency") + parser.add_argument( + "--local-status-max-age-seconds", + type=float, + default=DEFAULT_LOCAL_STATUS_MAX_AGE_SECONDS, + help="maximum cached local status age when --status-url is not used", + ) + parser.add_argument("--pool-metrics-url", default=DEFAULT_POOL_METRICS_URL, help="optional pool Prometheus /metrics URL") + parser.add_argument("--no-pool-metrics", action="store_true", help="skip pool Prometheus metrics collection") parser.add_argument("--label", default="baseline") parser.add_argument("--output-dir", default=str(RUNTIME_DIR / "measurements")) parser.add_argument("--json", action="store_true", help="print JSON summary") args = parser.parse_args() + if args.no_pool_metrics: + args.pool_metrics_url = None result = run_measurement(args) if args.json: print(json.dumps(result, indent=2, sort_keys=True)) diff --git a/ops/pipeline_profiler.py b/ops/pipeline_profiler.py new file mode 100644 index 0000000..f127db4 --- /dev/null +++ b/ops/pipeline_profiler.py @@ -0,0 +1,790 @@ +#!/usr/bin/env python3 +"""Profile the live chain -> pool -> ASIC -> pool -> chain timing pipeline. + +This collector is intentionally read-only. It stores enough correlated timing +evidence to compare code/config states against paid-block conversion instead of +optimizing from one metric in isolation. +""" + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import time +import urllib.request +from pathlib import Path +from typing import Any + +from optimization_measurement import ( + metric_max, + metric_sum, + metric_sum_by_label, + parse_prometheus_metrics, +) +from pool_ops import RUNTIME_DIR, now_iso, seconds_since_epoch + + +DEFAULT_STATUS_URL = "http://127.0.0.1:8088/api/status" +DEFAULT_GLOBAL_PAID_URL = "http://127.0.0.1:8088/api/live/global-pool-earnings" +DEFAULT_POOL_METRICS_URL = "http://127.0.0.1:9090/metrics" +DEFAULT_JOB_STATE_URL = "http://127.0.0.1:9090/health/job-state" +DEFAULT_ASIC_PERFORMANCE_URL = "http://127.0.0.1:9090/health/asic-performance" +DEFAULT_OUTPUT_DIR = RUNTIME_DIR / "profiling" +PROJECT_ROOT = Path(__file__).resolve().parents[1] + +POOL_X100_COUNTS = { + "0x1719e0ee598c15957448d5e568948101df78e7a0": 4, + "0x1719...e7a0": 4, + "0xd5f8df0a60bc1ff4636250b2a2dff319bf79b1f5": 2, + "0xd5f8...b1f5": 2, + "0x94390581d27ac0faf4792984068e9a4366e3ebe0": 1, + "0x9439...ebe0": 1, +} +LOCAL_POOL = "0x1719e0ee598c15957448d5e568948101df78e7a0" +BENCHMARK_POOLS = { + "b1f5": "0xd5f8df0a60bc1ff4636250b2a2dff319bf79b1f5", + "ebe0": "0x94390581d27ac0faf4792984068e9a4366e3ebe0", +} + +NODE_LOG_PATTERNS = ( + "Rewinding blockchain", + "Recovered mining template", + "broadcast block failed", + "nonce too low", + "nonce too high", + "request error", +) +POOL_LOG_PATTERNS = ( + "Block submitted successfully", + "Block submission too late", + "template refresh backend not ready", + "node template invalidation", + "STALE JOB", + "duplicate", + "stale-parent", + "tip-overdue", + "node-syncing", + "invalidated 4 current miner jobs", + "preserving current jobs", +) + + +def safe_float(value: Any) -> float | None: + try: + if value is None or value == "": + return None + return float(value) + except (TypeError, ValueError): + return None + + +def run_cmd(command: list[str], *, cwd: Path = PROJECT_ROOT, timeout: float = 10.0) -> dict[str, Any]: + started = time.monotonic() + try: + proc = subprocess.run( + command, + cwd=str(cwd), + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + timeout=timeout, + check=False, + ) + except Exception as exc: # noqa: BLE001 - profiler should continue. + return { + "ok": False, + "error": str(exc), + "latency_ms": round((time.monotonic() - started) * 1000, 3), + } + return { + "ok": proc.returncode == 0, + "returncode": proc.returncode, + "stdout": proc.stdout, + "stderr": proc.stderr, + "latency_ms": round((time.monotonic() - started) * 1000, 3), + } + + +def fetch_json(url: str, timeout: float = 5.0) -> tuple[dict[str, Any], float | None, str | None]: + started = time.monotonic() + try: + req = urllib.request.Request(url, headers={"accept": "application/json"}) + with urllib.request.urlopen(req, timeout=timeout) as response: + payload = json.loads(response.read().decode("utf-8")) + return payload if isinstance(payload, dict) else {}, round((time.monotonic() - started) * 1000, 3), None + except Exception as exc: # noqa: BLE001 + return {}, round((time.monotonic() - started) * 1000, 3), str(exc) + + +def fetch_text(url: str, timeout: float = 5.0) -> tuple[str, float | None, str | None]: + started = time.monotonic() + try: + req = urllib.request.Request(url, headers={"accept": "text/plain"}) + with urllib.request.urlopen(req, timeout=timeout) as response: + return response.read().decode("utf-8", "replace"), round((time.monotonic() - started) * 1000, 3), None + except Exception as exc: # noqa: BLE001 + return "", round((time.monotonic() - started) * 1000, 3), str(exc) + + +def metric_rows(metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], name: str) -> list[tuple[dict[str, str], float]]: + return [(dict(labels), value) for (metric_name, labels), value in metrics.items() if metric_name == name] + + +def histogram_average_by_label( + metrics: dict[tuple[str, tuple[tuple[str, str], ...]], float], + base_name: str, + group_label: str | None = None, +) -> dict[str, dict[str, float]]: + sums: dict[str, float] = {} + counts: dict[str, float] = {} + for labels, value in metric_rows(metrics, base_name + "_sum"): + key = labels.get(group_label, "total") if group_label else "total" + sums[key] = sums.get(key, 0.0) + value + for labels, value in metric_rows(metrics, base_name + "_count"): + key = labels.get(group_label, "total") if group_label else "total" + counts[key] = counts.get(key, 0.0) + value + out: dict[str, dict[str, float]] = {} + for key in sorted(set(sums) | set(counts)): + count = counts.get(key, 0.0) + total = sums.get(key, 0.0) + out[key] = { + "count": round(count, 6), + "sum_seconds": round(total, 6), + "avg_seconds": round(total / count, 6) if count > 0 else 0.0, + } + return out + + +def compact_metrics(text: str, latency_ms: float | None, error: str | None) -> dict[str, Any]: + metrics = parse_prometheus_metrics(text) if text else {} + rejected = 0.0 + for labels, value in metric_rows(metrics, "pool_block_submit_outcomes_total"): + if labels.get("outcome") != "accepted": + rejected += value + return { + "latency_ms": latency_ms, + "error": error, + "active_connections": metric_sum(metrics, "pool_active_connections"), + "authorized_miners": metric_sum(metrics, "pool_job_health_authorized_miners"), + "ready_miners": metric_sum(metrics, "pool_job_health_ready_miners"), + "current_job_age_seconds_max": metric_max(metrics, "pool_job_health_max_current_job_age_seconds"), + "current_job_invalidated_miners": metric_sum(metrics, "pool_job_health_current_job_invalidated_miners"), + "current_job_stale_miners": metric_sum(metrics, "pool_job_health_current_job_stale_miners"), + "current_job_expired_miners": metric_sum(metrics, "pool_job_health_current_job_expired_miners"), + "template_broadcasts_total": metric_sum(metrics, "pool_template_broadcasts_total"), + "submit_accepted_total": metric_sum(metrics, "pool_block_submit_outcomes_total", {"outcome": "accepted"}), + "submit_rejected_total": round(rejected, 6), + "submit_rejected_by_reason": metric_sum_by_label( + metrics, + "pool_block_submit_outcomes_total", + "reason", + exclude_labels={"outcome": "accepted"}, + ), + "shares_accepted_total": metric_sum(metrics, "pool_shares_accepted_total"), + "shares_rejected_total": metric_sum(metrics, "pool_shares_rejected_total"), + "backend_mineable": metric_max(metrics, "pool_rpc_backend_node_health_mineable"), + "backend_submit_ready": metric_max(metrics, "pool_rpc_backend_node_health_submit_ready"), + "backend_p2p_fresh": metric_max(metrics, "pool_rpc_backend_node_health_p2p_mining_fresh"), + "backend_consensus_peers": metric_max(metrics, "pool_rpc_backend_node_health_p2p_consensus_peer_count"), + "backend_fresh_consensus_peers": metric_max(metrics, "pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count"), + "backend_best_peer_lead_blocks": metric_max(metrics, "pool_rpc_backend_node_health_p2p_best_peer_lead_blocks"), + "backend_best_peer_graph_state_age_seconds": metric_max( + metrics, + "pool_rpc_backend_node_health_p2p_best_peer_graph_state_age_seconds", + ), + "backend_template_age_seconds": metric_max(metrics, "pool_rpc_backend_node_health_template_age_seconds"), + "backend_template_sequence": metric_max(metrics, "pool_rpc_backend_node_health_template_sequence"), + "backend_last_template_build_age_seconds": metric_max( + metrics, + "pool_rpc_backend_node_health_last_template_build_age_seconds", + ), + "backend_last_template_build_duration_seconds": metric_max( + metrics, + "pool_rpc_backend_node_health_last_template_build_duration_seconds", + ), + "backend_pending_template_build": metric_max(metrics, "pool_rpc_backend_node_health_pending_template_build"), + "backend_pending_template_invalidation_by_cause": metric_sum_by_label( + metrics, + "pool_rpc_backend_node_health_pending_template_invalidation", + "cause", + ), + "template_invalidations_by_cause": metric_sum_by_label( + metrics, + "pool_rpc_backend_node_health_template_invalidations_total", + "cause", + ), + "template_conversion_failure_ratio": metric_max(metrics, "pool_template_conversion_stall_failure_ratio"), + "template_conversion_window": metric_sum_by_label( + metrics, + "pool_template_conversion_stall_window_candidates", + "kind", + ), + "timing_controller_job_age_ms": metric_max(metrics, "pool_block_timing_controller_job_age_ms"), + "timing_controller_template_ttl_ms": metric_max(metrics, "pool_block_timing_controller_template_ttl_ms"), + "timing_controller_stale_grace_ms": metric_max(metrics, "pool_block_timing_controller_recent_stale_grace_ms"), + "template_fetch_duration": histogram_average_by_label(metrics, "pool_template_fetch_duration_seconds"), + "rpc_submit_duration_by_result": histogram_average_by_label( + metrics, + "pool_rpc_backend_submit_duration_seconds", + "result", + ), + "candidate_reject_job_age_by_reason": histogram_average_by_label( + metrics, + "pool_block_candidate_reject_job_age_seconds", + "reason", + ), + } + + +def compact_status(status: dict[str, Any], latency_ms: float | None, error: str | None) -> dict[str, Any]: + sync = status.get("sync_progress") if isinstance(status.get("sync_progress"), dict) else {} + pool = status.get("pool_health") if isinstance(status.get("pool_health"), dict) else {} + miner = status.get("miner_health") if isinstance(status.get("miner_health"), dict) else {} + return { + "latency_ms": latency_ms, + "error": error, + "overall": status.get("overall"), + "mode": status.get("mode"), + "can_accept_shares": status.get("can_accept_shares"), + "can_submit_blocks": status.get("can_submit_blocks"), + "sync_status": sync.get("status"), + "native_is_current": sync.get("native_is_current"), + "chain_syncing": sync.get("chain_syncing"), + "mining_advisory_sync": sync.get("mining_advisory_sync"), + "current_block": sync.get("current_block"), + "highest_block": sync.get("highest_block"), + "chain_block_count": sync.get("chain_block_count"), + "p2p_connections": sync.get("p2p_connections") or sync.get("peer_count"), + "p2p_network_gap": sync.get("p2p_network_gap"), + "best_peer_mainorder": sync.get("best_peer_mainorder"), + "peer_mainorder_gap": sync.get("peer_mainorder_gap"), + "pool_source_job_health": pool.get("source_job_health"), + "miner_connected_count": miner.get("connected_count"), + "miner_managed_count": miner.get("managed_count"), + "miner_tracked_count": miner.get("tracked_count"), + } + + +def compact_job_state(job_state: dict[str, Any], latency_ms: float | None, error: str | None) -> dict[str, Any]: + clients = job_state.get("clients") if isinstance(job_state.get("clients"), list) else [] + compact_clients = [] + for client in clients: + if not isinstance(client, dict): + continue + compact_clients.append( + { + "mac": client.get("asic_mac") or client.get("mac"), + "worker": client.get("worker"), + "ready": client.get("ready"), + "health": client.get("health"), + "current_job_age_ms": client.get("current_job_age_ms"), + "current_job_id": client.get("current_job_id"), + "protocol_variant": client.get("protocol_variant"), + } + ) + return { + "latency_ms": latency_ms, + "error": error, + "status": job_state.get("status"), + "active_connections": job_state.get("active_connections"), + "authorized_connections": job_state.get("authorized_connections"), + "subscribed_connections": job_state.get("subscribed_connections"), + "ready_connections": job_state.get("ready_connections"), + "clients": compact_clients, + } + + +def compact_asic_performance(payload: dict[str, Any], latency_ms: float | None, error: str | None) -> dict[str, Any]: + lanes = payload.get("lanes") if isinstance(payload.get("lanes"), list) else [] + compact_lanes = [] + for lane in lanes: + if not isinstance(lane, dict): + continue + compact_lanes.append( + { + "lane_id": lane.get("lane_id"), + "asic_mac": lane.get("asic_mac"), + "remote_host": lane.get("remote_host"), + "worker": lane.get("worker"), + "ready": lane.get("ready"), + "health": lane.get("health"), + "current_job_age_ms": lane.get("current_job_age_ms"), + "pdiff": lane.get("pdiff"), + "protocol_variant": lane.get("protocol_variant"), + "blocks_30m": lane.get("blocks_30m"), + "rejected_30m": lane.get("rejected_30m"), + "rejected_local_30m": lane.get("rejected_local_30m"), + "last_accepted_unix_ms": lane.get("last_accepted_unix_ms"), + } + ) + return { + "latency_ms": latency_ms, + "error": error, + "generated_at_unix_ms": payload.get("generated_at_unix_ms"), + "window_seconds": payload.get("window_seconds"), + "bucket_seconds": payload.get("bucket_seconds"), + "lanes": compact_lanes, + } + + +def pool_x100_count(row: dict[str, Any]) -> int | None: + keys = [ + str(row.get("key") or "").lower(), + str(row.get("pool") or "").lower(), + str(row.get("address") or "").lower(), + ] + for key in keys: + if key in POOL_X100_COUNTS: + return POOL_X100_COUNTS[key] + for key in keys: + for known, count in POOL_X100_COUNTS.items(): + if key and (key.endswith(known[-4:]) or known.endswith(key[-4:])): + return count + return None + + +def parse_block_count(value: Any) -> int | None: + try: + if value is None: + return None + return int(str(value).replace(",", "").strip()) + except ValueError: + return None + + +def compact_global_paid(payload: dict[str, Any], latency_ms: float | None, error: str | None) -> dict[str, Any]: + rows = payload.get("rows") if isinstance(payload.get("rows"), list) else [] + tracked = [] + by_key: dict[str, dict[str, Any]] = {} + for row in rows: + if not isinstance(row, dict): + continue + x100 = pool_x100_count(row) + if not x100: + continue + key = str(row.get("key") or row.get("pool") or "").lower() + blocks = parse_block_count(row.get("blocks")) + entry = { + "key": key, + "pool": row.get("pool"), + "blocks": blocks, + "work": row.get("work"), + "x100_count": x100, + "blocks_per_x100": round(blocks / x100, 6) if blocks is not None and x100 > 0 else None, + "local": str(row.get("local")).lower() == "true", + "last": row.get("last"), + } + tracked.append(entry) + if key: + by_key[key] = entry + local = next((row for row in tracked if row["key"] == LOCAL_POOL or row["local"]), None) + benchmark_ratios: dict[str, Any] = {} + if local and local.get("blocks") is not None: + local_blocks = float(local["blocks"]) + local_x100 = float(local["x100_count"] or 0) + for name, full_key in BENCHMARK_POOLS.items(): + benchmark = by_key.get(full_key) + if not benchmark or benchmark.get("blocks_per_x100") in (None, 0): + continue + expected = float(benchmark["blocks_per_x100"]) * local_x100 + benchmark_ratios[name] = { + "benchmark_blocks_per_x100": benchmark["blocks_per_x100"], + "local_expected_blocks_at_benchmark_rate": round(expected, 6), + "local_vs_benchmark_rate": round(local_blocks / expected, 6) if expected > 0 else None, + } + return { + "latency_ms": latency_ms, + "error": error, + "updated_at": payload.get("updated_at"), + "title": payload.get("title"), + "tracked_rows": tracked, + "local_vs_benchmarks": benchmark_ratios, + } + + +def submission_timing_sql(window_seconds: int) -> str: + window_seconds = max(1, int(window_seconds)) + return f""" +WITH rows AS ( + SELECT * + FROM block_submissions + WHERE created_at >= now() - interval '{window_seconds} seconds' +), +outcomes AS ( + SELECT + concat(CASE WHEN accepted THEN 'accepted' ELSE 'rejected' END, ':', coalesce(outcome, 'unknown')) AS key, + count(*) AS count + FROM rows + GROUP BY 1 +), +asic AS ( + SELECT + coalesce(asic_mac, 'unknown') AS asic_mac, + count(*) FILTER (WHERE accepted) AS accepted, + count(*) FILTER (WHERE NOT accepted) AS rejected, + round(avg(job_age_ms)::numeric, 3) AS job_age_ms_avg, + round((percentile_cont(0.50) WITHIN GROUP (ORDER BY job_age_ms))::numeric, 3) AS job_age_ms_p50, + round((percentile_cont(0.95) WITHIN GROUP (ORDER BY job_age_ms))::numeric, 3) AS job_age_ms_p95, + max(job_age_ms) AS job_age_ms_max + FROM rows + WHERE job_age_ms IS NOT NULL + GROUP BY 1 +) +SELECT jsonb_build_object( + 'window_seconds', {window_seconds}, + 'row_count', (SELECT count(*) FROM rows), + 'accepted', (SELECT count(*) FILTER (WHERE accepted) FROM rows), + 'rejected', (SELECT count(*) FILTER (WHERE NOT accepted) FROM rows), + 'job_age_ms', ( + SELECT jsonb_build_object( + 'avg', round(avg(job_age_ms)::numeric, 3), + 'p50', round((percentile_cont(0.50) WITHIN GROUP (ORDER BY job_age_ms))::numeric, 3), + 'p95', round((percentile_cont(0.95) WITHIN GROUP (ORDER BY job_age_ms))::numeric, 3), + 'max', max(job_age_ms) + ) + FROM rows + WHERE job_age_ms IS NOT NULL + ), + 'by_outcome', coalesce((SELECT jsonb_object_agg(key, count) FROM outcomes), '{{}}'::jsonb), + 'by_asic', coalesce(( + SELECT jsonb_agg( + jsonb_build_object( + 'asic_mac', asic_mac, + 'accepted', accepted, + 'rejected', rejected, + 'job_age_ms_avg', job_age_ms_avg, + 'job_age_ms_p50', job_age_ms_p50, + 'job_age_ms_p95', job_age_ms_p95, + 'job_age_ms_max', job_age_ms_max + ) + ORDER BY asic_mac + ) + FROM asic + ), '[]'::jsonb) +)::text; +""".strip() + + +def parse_psql_json(text: str) -> dict[str, Any]: + for raw in reversed(text.splitlines()): + line = raw.strip() + if not line: + continue + try: + payload = json.loads(line) + except json.JSONDecodeError: + continue + return payload if isinstance(payload, dict) else {} + return {} + + +def collect_db_submission_timing(window_seconds: int, timeout: float) -> dict[str, Any]: + result = run_cmd( + [ + "docker", + "compose", + "exec", + "-T", + "pool-db", + "psql", + "-U", + "bdag_pool", + "-d", + "bdagpool", + "-t", + "-A", + "-c", + submission_timing_sql(window_seconds), + ], + timeout=timeout, + ) + payload = parse_psql_json(result.get("stdout") or "") + payload["latency_ms"] = result.get("latency_ms") + if not result.get("ok"): + payload["error"] = (result.get("stderr") or result.get("stdout") or result.get("error") or "")[-2000:] + return payload + + +def count_patterns(text: str, patterns: tuple[str, ...]) -> dict[str, int]: + return {pattern: len(re.findall(re.escape(pattern), text)) for pattern in patterns} + + +def collect_log_counts(service: str, lookback_seconds: int, patterns: tuple[str, ...], timeout: float) -> dict[str, Any]: + result = run_cmd( + ["docker", "compose", "logs", f"--since={max(1, int(lookback_seconds))}s", "--no-color", service], + timeout=timeout, + ) + text = result.get("stdout") or "" + return { + "latency_ms": result.get("latency_ms"), + "error": None if result.get("ok") else (result.get("stderr") or result.get("error") or "")[-1000:], + "line_count": len(text.splitlines()), + "counts": count_patterns(text, patterns), + } + + +def collect_docker_stats(timeout: float) -> dict[str, Any]: + result = run_cmd( + [ + "docker", + "stats", + "--no-stream", + "--format", + "{{json .}}", + "node", + "pool", + "dashboard", + "postgres", + "status-sampler", + ], + timeout=timeout, + ) + rows = [] + for line in (result.get("stdout") or "").splitlines(): + try: + rows.append(json.loads(line)) + except json.JSONDecodeError: + continue + return { + "latency_ms": result.get("latency_ms"), + "error": None if result.get("ok") else (result.get("stderr") or result.get("error") or "")[-1000:], + "rows": rows, + } + + +def repo_state(path: Path) -> dict[str, Any]: + if not path.exists(): + return {"path": str(path), "exists": False} + branch = run_cmd(["git", "branch", "--show-current"], cwd=path, timeout=5) + head = run_cmd(["git", "rev-parse", "--short", "HEAD"], cwd=path, timeout=5) + status = run_cmd(["git", "status", "--short"], cwd=path, timeout=5) + return { + "path": str(path), + "exists": True, + "branch": (branch.get("stdout") or "").strip(), + "head": (head.get("stdout") or "").strip(), + "dirty": bool((status.get("stdout") or "").strip()), + "status": (status.get("stdout") or status.get("stderr") or "")[:4000], + } + + +def write_manifest(session_dir: Path, args: argparse.Namespace) -> None: + repos = { + "stack_redis": PROJECT_ROOT, + "pool": Path("/home/jeremy/blockdag-source/pool"), + "blockdag_corechain": Path("/home/jeremy/blockdag-source/blockdag-corechain"), + "redis_dash": Path("/home/jeremy/blockdag-source/redis-dash"), + } + manifest = { + "document_type": "bdag_pipeline_profile_manifest", + "generated_at": now_iso(), + "label": args.label, + "duration_seconds": args.duration_seconds, + "interval_seconds": args.interval_seconds, + "db_window_seconds": args.db_window_seconds, + "log_lookback_seconds": args.log_lookback_seconds, + "urls": { + "status": args.status_url, + "global_paid": args.global_paid_url, + "pool_metrics": args.pool_metrics_url, + "job_state": args.job_state_url, + "asic_performance": args.asic_performance_url, + }, + "benchmark_x100_counts": { + "0x1719...e7a0": 4, + "0xd5f8...b1f5": 2, + "0x9439...ebe0": 1, + }, + "repos": {name: repo_state(path) for name, path in repos.items()}, + "read_only": True, + } + (session_dir / "manifest.json").write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def counter_delta(first: dict[str, Any], last: dict[str, Any], key: str) -> float | None: + first_value = safe_float(first.get(key)) + last_value = safe_float(last.get(key)) + if first_value is None or last_value is None: + return None + if last_value < first_value: + return round(last_value, 6) + return round(last_value - first_value, 6) + + +def counter_delta_map(first: dict[str, Any], last: dict[str, Any], key: str) -> dict[str, float]: + first_map = first.get(key) if isinstance(first.get(key), dict) else {} + last_map = last.get(key) if isinstance(last.get(key), dict) else {} + out: dict[str, float] = {} + for item_key in sorted(set(first_map) | set(last_map)): + old = safe_float(first_map.get(item_key)) or 0.0 + new = safe_float(last_map.get(item_key)) or 0.0 + delta = new if new < old else new - old + if delta: + out[item_key] = round(delta, 6) + return out + + +def summarize_samples(samples: list[dict[str, Any]]) -> dict[str, Any]: + if not samples: + return {"status": "empty", "sample_count": 0, "generated_at": now_iso()} + first = samples[0] + last = samples[-1] + elapsed = max(0.0, float(last.get("sampled_epoch") or 0) - float(first.get("sampled_epoch") or 0)) + first_metrics = first.get("metrics") if isinstance(first.get("metrics"), dict) else {} + last_metrics = last.get("metrics") if isinstance(last.get("metrics"), dict) else {} + accepted_delta = counter_delta(first_metrics, last_metrics, "submit_accepted_total") + rejected_delta = counter_delta(first_metrics, last_metrics, "submit_rejected_total") + broadcast_delta = counter_delta(first_metrics, last_metrics, "template_broadcasts_total") + job_age_p95_values = [] + ready_values = [] + submit_ready_values = [] + p2p_fresh_values = [] + for sample in samples: + db = sample.get("db_submission_timing") if isinstance(sample.get("db_submission_timing"), dict) else {} + age = db.get("job_age_ms") if isinstance(db.get("job_age_ms"), dict) else {} + p95 = safe_float(age.get("p95")) + if p95 is not None: + job_age_p95_values.append(p95) + metrics = sample.get("metrics") if isinstance(sample.get("metrics"), dict) else {} + for values, key in ( + (ready_values, "ready_miners"), + (submit_ready_values, "backend_submit_ready"), + (p2p_fresh_values, "backend_p2p_fresh"), + ): + value = safe_float(metrics.get(key)) + if value is not None: + values.append(value) + return { + "status": "ok", + "generated_at": now_iso(), + "sample_count": len(samples), + "first_sample_at": first.get("sampled_at"), + "last_sample_at": last.get("sampled_at"), + "elapsed_seconds": elapsed, + "accepted_submit_delta": accepted_delta, + "rejected_submit_delta": rejected_delta, + "rejected_per_accepted": round(rejected_delta / accepted_delta, 6) + if accepted_delta and rejected_delta is not None + else None, + "submit_rejected_by_reason_delta": counter_delta_map( + first_metrics, + last_metrics, + "submit_rejected_by_reason", + ), + "template_broadcast_delta": broadcast_delta, + "template_invalidations_by_cause_delta": counter_delta_map( + first_metrics, + last_metrics, + "template_invalidations_by_cause", + ), + "ready_miners_min": min(ready_values) if ready_values else None, + "backend_submit_ready_min": min(submit_ready_values) if submit_ready_values else None, + "backend_p2p_fresh_min": min(p2p_fresh_values) if p2p_fresh_values else None, + "db_job_age_p95_ms_max": max(job_age_p95_values) if job_age_p95_values else None, + "accepted_submit_per_hour": round(accepted_delta * 3600.0 / elapsed, 3) + if accepted_delta is not None and elapsed > 0 + else None, + "latest_global_paid": last.get("global_paid"), + "latest_db_submission_timing": last.get("db_submission_timing"), + } + + +def write_summary(session_dir: Path, samples: list[dict[str, Any]]) -> None: + (session_dir / "summary.json").write_text( + json.dumps(summarize_samples(samples), indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + + +def collect_sample(args: argparse.Namespace) -> dict[str, Any]: + status, status_latency, status_error = fetch_json(args.status_url, args.timeout_seconds) + global_paid, global_latency, global_error = fetch_json(args.global_paid_url, args.timeout_seconds) + job_state, job_latency, job_error = fetch_json(args.job_state_url, args.timeout_seconds) + asic, asic_latency, asic_error = fetch_json(args.asic_performance_url, args.timeout_seconds) + metrics_text, metrics_latency, metrics_error = fetch_text(args.pool_metrics_url, args.timeout_seconds) + return { + "sampled_at": now_iso(), + "sampled_epoch": seconds_since_epoch(), + "status": compact_status(status, status_latency, status_error), + "global_paid": compact_global_paid(global_paid, global_latency, global_error), + "job_state": compact_job_state(job_state, job_latency, job_error), + "asic_performance": compact_asic_performance(asic, asic_latency, asic_error), + "metrics": compact_metrics(metrics_text, metrics_latency, metrics_error), + "db_submission_timing": collect_db_submission_timing(args.db_window_seconds, args.timeout_seconds), + "logs": { + "node": collect_log_counts("node", args.log_lookback_seconds, NODE_LOG_PATTERNS, args.timeout_seconds), + "pool": collect_log_counts("pool", args.log_lookback_seconds, POOL_LOG_PATTERNS, args.timeout_seconds), + }, + "docker_stats": collect_docker_stats(args.timeout_seconds), + } + + +def safe_label(label: str) -> str: + cleaned = "".join(ch if ch.isalnum() or ch in "-_" else "-" for ch in label.strip()) + return cleaned or "pipeline-profile" + + +def run_profiler(args: argparse.Namespace) -> dict[str, Any]: + stamp = time.strftime("%Y%m%d-%H%M%S") + session_dir = Path(args.output_dir).expanduser() / f"{safe_label(args.label)}-{stamp}" + session_dir.mkdir(parents=True, exist_ok=True) + jsonl_path = session_dir / "samples.jsonl" + latest_path = Path(args.output_dir).expanduser() / "latest-pipeline-profiler.txt" + latest_path.parent.mkdir(parents=True, exist_ok=True) + latest_path.write_text(str(session_dir) + "\n", encoding="utf-8") + write_manifest(session_dir, args) + + samples: list[dict[str, Any]] = [] + deadline = time.monotonic() + max(0.0, args.duration_seconds) + while True: + sample = collect_sample(args) + samples.append(sample) + with jsonl_path.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(sample, sort_keys=True) + "\n") + write_summary(session_dir, samples) + if args.print_samples: + print(json.dumps(sample, sort_keys=True), flush=True) + if args.duration_seconds <= 0 or time.monotonic() >= deadline: + break + sleep_for = min(max(1.0, args.interval_seconds), max(0.0, deadline - time.monotonic())) + if sleep_for > 0: + time.sleep(sleep_for) + return { + "session_dir": str(session_dir), + "jsonl_path": str(jsonl_path), + "summary_path": str(session_dir / "summary.json"), + "manifest_path": str(session_dir / "manifest.json"), + "latest_path": str(latest_path), + "summary": summarize_samples(samples), + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--duration-seconds", type=float, default=5 * 60 * 60) + parser.add_argument("--interval-seconds", type=float, default=30.0) + parser.add_argument("--timeout-seconds", type=float, default=8.0) + parser.add_argument("--db-window-seconds", type=int, default=300) + parser.add_argument("--log-lookback-seconds", type=int, default=60) + parser.add_argument("--status-url", default=DEFAULT_STATUS_URL) + parser.add_argument("--global-paid-url", default=DEFAULT_GLOBAL_PAID_URL) + parser.add_argument("--pool-metrics-url", default=DEFAULT_POOL_METRICS_URL) + parser.add_argument("--job-state-url", default=DEFAULT_JOB_STATE_URL) + parser.add_argument("--asic-performance-url", default=DEFAULT_ASIC_PERFORMANCE_URL) + parser.add_argument("--output-dir", default=str(DEFAULT_OUTPUT_DIR)) + parser.add_argument("--label", default="pipeline-profile") + parser.add_argument("--print-samples", action="store_true") + parser.add_argument("--json", action="store_true", help="print final JSON summary") + args = parser.parse_args() + result = run_profiler(args) + if args.json: + print(json.dumps(result, indent=2, sort_keys=True)) + else: + print(result["session_dir"]) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/ops/pool_ops.py b/ops/pool_ops.py index 0e31eb1..3db8cf1 100644 --- a/ops/pool_ops.py +++ b/ops/pool_ops.py @@ -253,11 +253,14 @@ def host_runtime_profile(force_refresh: bool = False) -> dict[str, Any]: STATUS_SAMPLER_FILE = RUNTIME_DIR / "status-sampler.json" SYNC_PROGRESS_HEALTH_STATE_FILE = RUNTIME_DIR / "sync-progress-health-state.json" EVM_REFERENCE_GAP_WATCH_FILE = RUNTIME_DIR / "evm-reference-gap-watch.json" +POOL_PAID_WORK_STATE_FILE = RUNTIME_DIR / "pool-paid-work-state.json" +TEMPLATE_SAFETY_STATE_FILE = RUNTIME_DIR / "template-safety-state.json" STATUS_PAYLOAD_STALE_AFTER_SECONDS = env_float( "BDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS", 120.0, minimum=5.0, ) +POOL_PAID_WORK_RECENT_SECONDS = env_float("BDAG_POOL_PAID_WORK_RECENT_SECONDS", 60.0, minimum=5.0) CATCHUP_PAUSE_ENABLED = env_bool("BDAG_CATCHUP_PAUSE_ENABLED", True) CATCHUP_PAUSE_THRESHOLD_BLOCKS = env_int("BDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS", 300, minimum=1) CATCHUP_NODE_CACHE_MB = env_int("BDAG_CATCHUP_NODE_CACHE_MB", 6144, minimum=0) @@ -266,6 +269,16 @@ def host_runtime_profile(force_refresh: bool = False) -> dict[str, Any]: CATCHUP_IOWAIT_WARN_PERCENT = env_float("BDAG_CATCHUP_IOWAIT_WARN_PERCENT", 15.0, minimum=0.0) CATCHUP_IO_SOME_AVG10_WARN = env_float("BDAG_CATCHUP_IO_SOME_AVG10_WARN", 20.0, minimum=0.0) CATCHUP_IO_FULL_AVG10_WARN = env_float("BDAG_CATCHUP_IO_FULL_AVG10_WARN", 10.0, minimum=0.0) +CATCHUP_NATIVE_P2P_MIN_FRESH_CONSENSUS_PEERS = env_int( + "BDAG_CATCHUP_NATIVE_P2P_MIN_FRESH_CONSENSUS_PEERS", + 2, + minimum=1, +) +CATCHUP_NATIVE_P2P_MAX_PEER_LEAD_BLOCKS = env_int( + "BDAG_CATCHUP_NATIVE_P2P_MAX_PEER_LEAD_BLOCKS", + pool_start_gate.MAX_NATIVE_PEER_LEAD_BLOCKS, + minimum=0, +) SYNC_PROGRESS_ACTIVE_LOOKBACK_SECONDS = int(os.environ.get("BDAG_SYNC_PROGRESS_ACTIVE_LOOKBACK_SECONDS", "2700")) DEFAULT_POOL_ENV_FILE = PROJECT_ROOT / ".env" POOL_ENV_FILE = path_from_env("BDAG_POOL_ENV_FILE", DEFAULT_POOL_ENV_FILE, PROJECT_ROOT) @@ -574,10 +587,14 @@ def is_no_miner_sync_noise(item: Any) -> bool: def is_recent_mining_sync_noise(item: Any) -> bool: text = str(item) + lowered = text.lower() return ( "live mining template probes" in text or "pool recently saw RPC connection refused" in text or text.startswith("pool is waiting for node sync to finish") + or "reports node busy syncing" in lowered + or "selected pool backend is still catching up by" in lowered + or "is still catching up by" in lowered ) @@ -761,6 +778,164 @@ def effective_connected_miner_count( ) +def pool_metrics_accepted_block_submissions(pool_metrics: Mapping[str, Any]) -> float: + block_outcomes = pool_metrics.get("block_submit_outcomes") if isinstance(pool_metrics, Mapping) else {} + if not isinstance(block_outcomes, Mapping): + return 0.0 + return sum( + _float_metric(value) + for key, value in block_outcomes.items() + if str(key).startswith("accepted:") + ) + + +def update_pool_paid_work_state( + metrics_accepted_block_submissions: float, + now_epoch: float | None = None, + metrics_available: bool = True, +) -> dict[str, Any]: + now_value = time.time() if now_epoch is None else float(now_epoch) + accepted_total = max(0.0, float(metrics_accepted_block_submissions or 0.0)) + previous = read_json_file(POOL_PAID_WORK_STATE_FILE, {}) + if not isinstance(previous, Mapping): + previous = {} + previous_total = safe_float(previous.get("accepted_block_submissions"), None) + last_accepted_at = safe_float(previous.get("last_accepted_at_epoch"), None) + if not metrics_available: + accepted_total = max(accepted_total, previous_total or 0.0) + elif accepted_total > 0 and ( + previous_total is None + or accepted_total > previous_total + or (previous_total > 0 and accepted_total < previous_total) + ): + last_accepted_at = now_value + elif accepted_total <= 0: + last_accepted_at = None + + age_seconds = None if last_accepted_at is None else max(0.0, now_value - last_accepted_at) + recent = bool(age_seconds is not None and age_seconds <= POOL_PAID_WORK_RECENT_SECONDS) + delta = ( + accepted_total + if previous_total is None + else max(0.0, accepted_total - max(0.0, previous_total)) + ) + payload = { + "generated_at": now_iso(), + "generated_epoch_seconds": now_value, + "accepted_block_submissions": _prometheus_json_number(accepted_total), + "accepted_block_submission_delta": _prometheus_json_number(delta), + "last_accepted_at_epoch": last_accepted_at, + "last_accepted_age_seconds": None if age_seconds is None else round(age_seconds, 3), + "accepted_block_recent": recent, + "recent_window_seconds": POOL_PAID_WORK_RECENT_SECONDS, + "metrics_available": bool(metrics_available), + } + try: + write_json_file(POOL_PAID_WORK_STATE_FILE, payload, mode=0o600) + except OSError: + pass + return payload + + +def selected_backend_mining_safe(selected_source_health: Mapping[str, Any] | None) -> bool: + if not isinstance(selected_source_health, Mapping) or not selected_source_health: + return False + if selected_source_health.get("healthy") is False: + return False + if selected_source_health.get("node_template_coinbase_valid") is False: + return False + if selected_source_health.get("node_mineable") is not True: + return False + if selected_source_health.get("node_submit_ready") is not True: + return False + if selected_source_health.get("node_p2p_mining_fresh") is not True: + return False + if not selected_backend_peer_floor_safe(selected_source_health): + return False + if not selected_backend_peer_lead_safe(selected_source_health): + return False + return True + + +def optional_bool(value: Any) -> bool | None: + if isinstance(value, bool): + return value + if isinstance(value, (int, float)): + return value > 0 + if isinstance(value, str): + normalized = value.strip().lower() + if normalized in {"1", "true", "yes", "y", "ok"}: + return True + if normalized in {"0", "false", "no", "n", "bad"}: + return False + return None + + +def selected_backend_peer_floor_safe(selected_source_health: Mapping[str, Any]) -> bool: + fresh_peers = safe_int(selected_source_health.get("node_p2p_fresh_consensus_peer_count"), -1) + if fresh_peers >= 0: + return fresh_peers >= CATCHUP_NATIVE_P2P_MIN_FRESH_CONSENSUS_PEERS + consensus_peers = safe_int(selected_source_health.get("node_p2p_consensus_peer_count"), -1) + if consensus_peers >= 0: + return consensus_peers >= CATCHUP_NATIVE_P2P_MIN_FRESH_CONSENSUS_PEERS + return False + + +def selected_backend_peer_lead_safe(selected_source_health: Mapping[str, Any]) -> bool: + peer_lead = safe_float(selected_source_health.get("node_p2p_best_peer_lead_blocks"), None) + return bool(peer_lead is not None and peer_lead <= CATCHUP_NATIVE_P2P_MAX_PEER_LEAD_BLOCKS) + + +def selected_backend_native_p2p_current_safe(selected_source_health: Mapping[str, Any] | None) -> bool: + if not isinstance(selected_source_health, Mapping) or not selected_source_health: + return False + if selected_source_health.get("healthy") is False: + return False + if selected_source_health.get("node_template_coinbase_valid") is False: + return False + if selected_source_health.get("node_p2p_mining_fresh") is not True: + return False + if not selected_backend_peer_floor_safe(selected_source_health): + return False + if not selected_backend_peer_lead_safe(selected_source_health): + return False + return True + + +def selected_backend_recent_paid_work_safe( + selected_source_health: Mapping[str, Any] | None, + pool_metrics: Mapping[str, Any] | None, + source_job_health: Mapping[str, Any] | None, + has_recent_paid_work_evidence: bool, + connected_miners: int, +) -> bool: + if not has_recent_paid_work_evidence: + return False + if not isinstance(selected_source_health, Mapping) or not selected_source_health: + return False + if selected_source_health.get("healthy") is False: + return False + if selected_source_health.get("node_template_coinbase_valid") is False: + return False + if selected_source_health.get("node_p2p_mining_fresh") is not True: + return False + if selected_source_health.get("node_last_template_build_error_blocking") is True: + return False + if not selected_backend_peer_floor_safe(selected_source_health): + return False + if not selected_backend_peer_lead_safe(selected_source_health): + return False + metrics = pool_metrics if isinstance(pool_metrics, Mapping) else {} + job_health = source_job_health if isinstance(source_job_health, Mapping) else {} + mining_connections = max( + safe_int(connected_miners, 0), + safe_int(metrics.get("active_connections"), 0), + safe_int(job_health.get("authorized_miners"), 0), + safe_int(job_health.get("ready_miners"), 0), + ) + return mining_connections > 0 + + def miner_failures_block_stack( miner_failures: list[str], connected_miners: int, @@ -782,6 +957,7 @@ def miner_failures_block_stack( def selected_backend_unready_reasons(selected_source_health: Mapping[str, Any]) -> list[str]: reasons: list[str] = [] for key, label in ( + ("node_template_coinbase_valid", "template_coinbase_valid=false"), ("node_mineable", "mineable=false"), ("node_submit_ready", "submit_ready=false"), ("node_p2p_mining_fresh", "p2p_mining_fresh=false"), @@ -1216,8 +1392,11 @@ def docker_compose_command(*args: str) -> list[str]: command.extend([ "-f", str(PROJECT_ROOT / "docker-compose.yml"), - *args, ]) + override = PROJECT_ROOT / "docker-compose.override.yml" + if override.exists(): + command.extend(["-f", str(override)]) + command.extend(args) return command @@ -2511,13 +2690,17 @@ def get_miner_cgminer_devs(ip: str, timeout: float = MINER_HTTP_TIMEOUT) -> dict def discover_miner(ip: str, timeout: float = MINER_SCAN_TIMEOUT) -> dict[str, Any] | None: started = time.time() + pools: list[dict[str, Any]] = [] + pool_api_error = "" try: pools = get_miner_pools(ip, timeout=timeout) - except MinerAPIError: - return None + except MinerAPIError as exc: + pool_api_error = str(exc) status = get_miner_status(ip, timeout=timeout) settings = get_miner_settings(ip, timeout=timeout) + if not pools and not status and not settings: + return None identity_payload = {**settings, **status} active_pool = next((pool for pool in pools if pool.get("active")), pools[0] if pools else {}) mac = miner_mac_from_payload(identity_payload, ip) @@ -2532,6 +2715,7 @@ def discover_miner(ip: str, timeout: float = MINER_SCAN_TIMEOUT) -> dict[str, An "mcbversion": status.get("mcbversion", ""), "pool_count": len(pools), "active": bool(active_pool.get("active")), + "pool_api_error": pool_api_error, "current_pool": active_pool, "pools": pools, "response_ms": round((time.time() - started) * 1000), @@ -3585,6 +3769,103 @@ def _prometheus_counter_json(counter: Counter[str] | dict[str, Any]) -> dict[str return {str(key): _prometheus_json_number(_float_metric(value)) for key, value in sorted(rows)} +def counter_total_matching(counter: Mapping[str, Any], predicate) -> float: + total = 0.0 + for key, value in counter.items(): + if predicate(str(key)): + total += _float_metric(value) + return total + + +def zero_coinbase_reject_total(rejects: Mapping[str, Any]) -> float: + return counter_total_matching(rejects, lambda key: "zero-template-address" in key) + + +def update_zero_coinbase_reject_rate(total: float, metrics_available: bool) -> dict[str, Any]: + now_value = seconds_since_epoch() + previous = read_json_file(TEMPLATE_SAFETY_STATE_FILE, {}) + if not isinstance(previous, Mapping): + previous = {} + previous_total = safe_float(previous.get("zero_coinbase_reject_total"), None) + previous_epoch = safe_float(previous.get("generated_epoch_seconds"), None) + elapsed = max(0.0, now_value - previous_epoch) if previous_epoch is not None else None + delta = None + rate = None + if previous_total is not None and elapsed and elapsed > 0: + delta = max(0.0, total - previous_total) if total >= previous_total else total + rate = delta / elapsed + + payload = { + "generated_at": now_iso(), + "generated_epoch_seconds": now_value, + "zero_coinbase_reject_total": _prometheus_json_number(total), + "zero_coinbase_reject_delta": _prometheus_json_number(delta) if delta is not None else None, + "zero_coinbase_reject_rate_per_second": round(rate, 6) if rate is not None else None, + "sample_elapsed_seconds": round(elapsed, 3) if elapsed is not None else None, + } + if metrics_available: + try: + write_json_file(TEMPLATE_SAFETY_STATE_FILE, payload, mode=0o600) + except OSError: + pass + return payload + + +def local_stale_block_submit_stats(block_submit_outcomes: Mapping[str, Any]) -> dict[str, Any]: + accepted = counter_total_matching(block_submit_outcomes, lambda key: key.startswith("accepted:")) + local_stale = counter_total_matching( + block_submit_outcomes, + lambda key: ( + key.startswith("rejected-local:stale-job") + or key.startswith("rejected-local:stale-parent") + or key.startswith("rejected-local:parent-not-current") + or key.startswith("rejected-local:parent_not_current") + ), + ) + denominator = accepted + local_stale + return { + "accepted": _prometheus_json_number(accepted), + "local_stale": _prometheus_json_number(local_stale), + "local_stale_ratio": round(local_stale / denominator, 6) if denominator > 0 else None, + } + + +def automation_restart_marker(latest_action: dict[str, Any] | None) -> dict[str, Any]: + if not isinstance(latest_action, dict): + return {"recent": False} + mode = str(latest_action.get("mode") or "") + name = str(latest_action.get("name") or "") + restart = mode in {"restart", "restart-node"} or name.startswith("restart-") + return { + "recent": restart, + "name": name, + "mode": mode, + "status": latest_action.get("status") or "", + "node": latest_action.get("node") or "", + "reason": latest_action.get("reason") or "", + "started_at": latest_action.get("started_at") or "", + "finished_at": latest_action.get("finished_at") or "", + } + + +def template_coinbase_address_valid(address: str) -> bool | None: + normalized = str(address or "").strip().lower() + if not normalized: + return None + return bool(valid_eth_address(normalized) and normalized != ZERO_ETH_ADDRESS) + + +def template_coinbase_valid_signal(value: Any, address: str = "", reason_code: str = "") -> bool | None: + parsed = optional_bool(value) + if parsed is not False: + return parsed + normalized_address = str(address or "").strip().lower() + normalized_reason = str(reason_code or "").strip().lower().replace("_", "-") + if normalized_address or normalized_reason == "zero-template-coinbase": + return False + return None + + def _ratio_percent(numerator: float, denominator: float) -> float | None: if denominator <= 0: return None @@ -3732,6 +4013,7 @@ def selected_backend_readiness_contract( selected_source_health: dict[str, Any], source_job_health: dict[str, Any], pool_has_recent_paid_work: bool, + readiness_override_safe: bool, ) -> dict[str, Any]: source = selected_source_health if isinstance(selected_source_health, dict) else {} job_health = source_job_health if isinstance(source_job_health, dict) else {} @@ -3750,18 +4032,22 @@ def selected_backend_readiness_contract( ) or checks.get("node_last_template_build_error_blocking_clear") is False job_unready = job_ok is False contradiction = bool(pool_has_recent_paid_work and (node_unready or job_unready)) - hard_unready = bool((node_unready or job_unready) and not pool_has_recent_paid_work) + hard_unready = bool((node_unready or job_unready) and not readiness_override_safe) return { "version": 1, "selected_backend": selected_backend, "pool_has_recent_mining": pool_has_recent_paid_work, "pool_has_recent_paid_work": pool_has_recent_paid_work, + "readiness_override_safe": readiness_override_safe, "job_health_ok": job_ok, "checks": checks, "contradiction": contradiction, "hard_unready": hard_unready, - "advisory_degraded": bool(contradiction), - "truth_basis": "only recent accepted block submission may override readiness; accepted shares alone are not paid mining", + "advisory_degraded": bool((node_unready or job_unready) and readiness_override_safe), + "truth_basis": ( + "recent accepted block submission may override mineable/submit readiness only when native " + "P2P freshness and peer-lead safety are intact; accepted shares alone are not paid mining" + ), } @@ -3775,6 +4061,43 @@ def selected_backend_source_degradation(selected_source_degraded: bool, pool_has } +def mark_sync_progress_mining_advisory(sync_progress: Mapping[str, Any], reason: str) -> dict[str, Any]: + payload = dict(sync_progress) + current = payload.get("current_block") + payload["status"] = "synced" + payload["percent"] = 100.0 + payload["highest_block"] = current if current is not None else payload.get("highest_block") + payload["remaining_blocks"] = 0 + payload["error"] = "" + payload["chain_syncing"] = False + payload["evm_chain_syncing"] = True + payload["mining_advisory_sync"] = True + payload["native_is_current"] = True + payload["evm_sync_advisory"] = reason + nodes = payload.get("nodes") + if isinstance(nodes, Mapping): + updated_nodes: dict[str, Any] = {} + for name, node_progress in nodes.items(): + if isinstance(node_progress, Mapping): + node_payload = dict(node_progress) + node_current = node_payload.get("current_block") + node_payload["status"] = "synced" + node_payload["percent"] = 100.0 + node_payload["highest_block"] = node_current if node_current is not None else node_payload.get("highest_block") + node_payload["remaining_blocks"] = 0 + node_payload["error"] = "" + node_payload["chain_syncing"] = False + node_payload["evm_chain_syncing"] = True + node_payload["mining_advisory_sync"] = True + node_payload["native_is_current"] = True + node_payload["evm_sync_advisory"] = reason + updated_nodes[str(name)] = node_payload + else: + updated_nodes[str(name)] = node_progress + payload["nodes"] = updated_nodes + return payload + + def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> dict[str, Any]: payload: dict[str, Any] = { "generated_at": now_iso(), @@ -3798,6 +4121,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di "source_backend_health": {}, "selected_backend_source_health": {}, "template_conversion_stall": {}, + "template_coinbase_rejects": {}, } if POOL_METRICS_PORT <= 0: payload["error"] = "pool metrics port disabled" @@ -3814,6 +4138,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di share_processing_count = 0.0 share_processing_sum = 0.0 submit_recoveries: Counter[str] = Counter() + template_coinbase_rejects: Counter[str] = Counter() selected_backend = "" active_connections: float | None = None source_job_health: dict[str, Any] = {} @@ -3875,7 +4200,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di "pool_rpc_backend_template_age_seconds", "pool_rpc_backend_ws_connected", }: - backend = labels.get("backend") + backend = labels.get("backend") or labels.get("node") if not backend: continue row = source_backend_health.setdefault(backend, {}) @@ -3892,7 +4217,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di elif metric_name == "pool_rpc_backend_ws_connected": row["ws_connected"] = value > 0 elif metric_name.startswith("pool_rpc_backend_node_health_"): - backend = labels.get("backend") + backend = labels.get("backend") or labels.get("node") if not backend: continue row = source_backend_health.setdefault(backend, {}) @@ -3900,6 +4225,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di if key in { "mineable", "submit_ready", + "template_coinbase_valid", "p2p_mining_fresh", "p2p_sync_peer_fresh", "p2p_sync_peer_present", @@ -3941,6 +4267,8 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di share_processing_sum += value elif metric_name == "pool_submit_stall_recoveries_total": submit_recoveries[_metric_counter_key(labels, "action", "reason")] += value + elif metric_name == "pool_template_coinbase_rejects_total": + template_coinbase_rejects[_metric_counter_key(labels, "source", "reason")] += value payload["containers"][name] = row if source_backend_health and not template_backend_source: template_backend_source = endpoint @@ -3962,6 +4290,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di } payload["submit_stall_recoveries"] = dict(submit_recoveries) payload["submit_stall_recoveries_total"] = float(sum(submit_recoveries.values())) + payload["template_coinbase_rejects"] = dict(template_coinbase_rejects) payload["source_job_health"] = source_job_health payload["source_backend_health"] = source_backend_health payload["template_conversion_stall"] = template_conversion_stall @@ -3971,6 +4300,7 @@ def collect_pool_prometheus_metrics(containers: dict[str, dict[str, Any]]) -> di "backends": source_backend_health, "job_health": source_job_health, "template_conversion_stall": template_conversion_stall, + "template_coinbase_rejects": dict(template_coinbase_rejects), "selected_backend": selected_backend, "backend_count": len(source_backend_health), "healthy_backend_count": sum(1 for row in source_backend_health.values() if row.get("healthy") is True), @@ -5169,9 +5499,12 @@ def max_catchup_lag_blocks( sync_progress: Mapping[str, Any], node_details: Mapping[str, Any], selected_source_health: Mapping[str, Any] | None = None, + *, + ignore_remaining_blocks: bool = False, ) -> int: values: list[int] = [] - for key in ("remaining_blocks", "peer_ahead_blocks"): + lag_keys = ("peer_ahead_blocks",) if ignore_remaining_blocks else ("remaining_blocks", "peer_ahead_blocks") + for key in lag_keys: value = safe_int(sync_progress.get(key), -1) if value >= 0: values.append(value) @@ -5179,14 +5512,14 @@ def max_catchup_lag_blocks( for info in progress_nodes.values(): if not isinstance(info, dict): continue - for key in ("remaining_blocks", "peer_ahead_blocks"): + for key in lag_keys: value = safe_int(info.get(key), -1) if value >= 0: values.append(value) for info in node_details.values(): if not isinstance(info, dict): continue - for key in ("remaining_blocks", "peer_ahead_blocks"): + for key in lag_keys: value = safe_int(info.get(key), -1) if value >= 0: values.append(value) @@ -5222,14 +5555,22 @@ def build_catchup_policy( selected_source_health: Mapping[str, Any] | None = None, host_pressure: Mapping[str, Any] | None = None, mining_ready: bool | None = None, + ignore_remaining_blocks: bool = False, ) -> dict[str, Any]: - lag = max_catchup_lag_blocks(sync_progress, node_details, selected_source_health) + lag = max_catchup_lag_blocks( + sync_progress, + node_details, + selected_source_health, + ignore_remaining_blocks=ignore_remaining_blocks, + ) status = str(sync_progress.get("status") or "").lower() peer_catchup = lag > 0 io_pressure_reasons = catchup_io_pressure_reasons(host_pressure) backend_unready_reasons = selected_backend_unready_reasons(selected_source_health or {}) - mining_ready_for_policy = bool(mining_ready) if mining_ready is not None else not bool( - backend_unready_reasons + mining_ready_for_policy = ( + bool(mining_ready) + if mining_ready is not None + else bool(selected_source_health and not backend_unready_reasons) ) backend_unready_under_pressure = bool( io_pressure_reasons @@ -5242,7 +5583,11 @@ def build_catchup_policy( and not mining_ready_for_policy and ((peer_catchup and lag >= CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS) or backend_unready_under_pressure) ) - lag_threshold_active = bool(lag > CATCHUP_PAUSE_THRESHOLD_BLOCKS and (status != "synced" or not mining_ready_for_policy)) + lag_threshold_active = bool( + lag > CATCHUP_PAUSE_THRESHOLD_BLOCKS + and status != "synced" + and not mining_ready_for_policy + ) active = bool(CATCHUP_PAUSE_ENABLED and (io_pressure_active or lag_threshold_active)) pool_running = bool((containers.get(POOL_CONTAINER) or {}).get("running")) if isinstance(containers, Mapping) else False trigger = ("io_pressure" if io_pressure_active else ("lag_threshold" if lag_threshold_active else "")) if active else "" @@ -5308,6 +5653,7 @@ def build_catchup_policy( "mining_ready": mining_ready_for_policy, "backend_unready_under_pressure": backend_unready_under_pressure, "backend_unready_reasons": backend_unready_reasons, + "remaining_blocks_advisory": ignore_remaining_blocks, "lag_threshold_active": lag_threshold_active, "pool_pause_recommended": active, "pool_pause_active": bool(active and not pool_running), @@ -5673,28 +6019,7 @@ def add_maintenance_warning(message: str) -> None: running_pool_containers = [name for name in POOL_CONTAINERS if containers.get(name, {}).get("running")] pool_log = docker_logs_many(running_pool_containers, lines=180) if include_logs and running_pool_containers else "" pool = parse_pool_log(pool_log) - pool_metrics = collect_pool_prometheus_metrics(containers) if include_logs else { - "generated_at": now_iso(), - "status": "skipped", - "error": "logs excluded from status collection", - "containers": {}, - "active_connections": None, - "selected_backend": "", - "block_submit_outcomes": {}, - "block_submit_backend_outcomes": {}, - "blocks": {}, - "blocks_rejected_by_node": {}, - "shares_accepted_total": 0.0, - "shares_rejected_by_reason": {}, - "share_processing": {}, - "loss_ledger": {}, - "submit_stall_recoveries": {}, - "submit_stall_recoveries_total": 0.0, - "source_job_health": {}, - "source_backend_health": {}, - "selected_backend_source_health": {}, - "template_conversion_stall": {}, - } + pool_metrics = collect_pool_prometheus_metrics(containers) pool["metrics"] = pool_metrics pool["selected_backend"] = pool_metrics.get("selected_backend") or "" pool["metrics_active_connections"] = pool_metrics.get("active_connections") @@ -5740,6 +6065,11 @@ def add_maintenance_warning(message: str) -> None: if isinstance(pool_metrics.get("template_conversion_stall"), dict) else {} ) + pool["template_coinbase_rejects"] = ( + pool_metrics.get("template_coinbase_rejects") + if isinstance(pool_metrics.get("template_coinbase_rejects"), dict) + else {} + ) pool_loss_ledger = ( pool_metrics.get("loss_ledger") if isinstance(pool_metrics.get("loss_ledger"), dict) @@ -5847,9 +6177,11 @@ def add_maintenance_warning(message: str) -> None: "planned_pause_leader": planned_pause_leader, } evm_reference_gap_watch = read_json_file(EVM_REFERENCE_GAP_WATCH_FILE, {}) + evm_reference_gap_restore_required = False if isinstance(evm_reference_gap_watch, dict) and evm_reference_gap_watch: sync_health["evm_reference_gap_watch"] = evm_reference_gap_watch if evm_reference_gap_watch.get("restore_required"): + evm_reference_gap_restore_required = True sync_health["evm_reference_gap_stalled"] = True sync_health["needs_chain_data_restore"] = True chain_blocker_nodes = { @@ -5901,24 +6233,111 @@ def add_maintenance_warning(message: str) -> None: ("last_valid_share_age_seconds", 60), ) ) - pool_has_recent_paid_work = bool( + metrics_accepted_block_submissions = pool_metrics_accepted_block_submissions(pool_metrics) + metrics_accepted_shares = _float_metric(pool_metrics.get("shares_accepted_total")) + pool_submit_metrics_available = bool( + pool_metrics.get("status") == "ok" + and isinstance(pool_metrics.get("block_submit_outcomes"), Mapping) + and pool_metrics.get("block_submit_outcomes") + ) + paid_work_state = update_pool_paid_work_state( + metrics_accepted_block_submissions, + metrics_available=pool_submit_metrics_available, + ) + if connected_miners > 0 and metrics_accepted_shares > 0: + pool_has_recent_share_activity = True + log_recent_paid_work = bool( safe_int(pool.get("block_submit_success_count"), 0) > 0 and pool.get("last_block_submit_age_seconds") is not None - and int(pool.get("last_block_submit_age_seconds") or 0) <= 60 + and int(pool.get("last_block_submit_age_seconds") or 0) <= POOL_PAID_WORK_RECENT_SECONDS + ) + metrics_recent_paid_work = bool( + connected_miners > 0 + and paid_work_state.get("accepted_block_recent") is True + and metrics_accepted_block_submissions > 0 + ) + raw_selected_source_mining_safe = selected_backend_mining_safe(selected_source_health) + recent_paid_backend_safe = selected_backend_recent_paid_work_safe( + selected_source_health, + pool_metrics, + source_job_health, + bool(log_recent_paid_work or metrics_recent_paid_work), + connected_miners, + ) + pool_has_recent_paid_work = bool( + log_recent_paid_work + or metrics_recent_paid_work + ) + sync_progress = sync_progress_for_display_nodes(collect_sync_progress(), display_nodes) + native_progress_paid_work_safe = bool( + connected_miners > 0 + and pool_has_recent_paid_work + and sync_progress_native_template_health_synced(sync_progress) + ) + native_p2p_current_safe = selected_backend_native_p2p_current_safe(selected_source_health) + native_chain_progress_safe = bool( + sync_progress_native_template_health_synced(sync_progress) + or native_p2p_current_safe + ) + readiness_override_safe = bool(recent_paid_backend_safe or native_progress_paid_work_safe) + selected_source_mining_safe = bool( + raw_selected_source_mining_safe + or recent_paid_backend_safe + or native_progress_paid_work_safe + ) + evm_reference_gap_advisory_safe = bool( + evm_reference_gap_restore_required + and pool_has_recent_paid_work + and selected_source_mining_safe + and not chain_blocker_nodes + and not chain_restore_nodes ) + if evm_reference_gap_advisory_safe: + sync_health.pop("evm_reference_gap_stalled", None) + if not sync_health.get("chain_state_blocker") and not sync_health.get("chain_data_restore_required"): + sync_health.pop("needs_chain_data_restore", None) + sync_health["evm_reference_gap_advisory"] = True + sync_health["evm_reference_gap_restore_suppressed_by_native_paid_work"] = True + if isinstance(evm_reference_gap_watch, dict): + sync_health["evm_reference_gap_watch"] = { + **evm_reference_gap_watch, + "restore_required": False, + "would_restore_required": True, + "restore_suppressed_by_native_paid_work": True, + "native_paid_work_advisory_reason": "selected backend is mining-safe and recent paid work is present", + } + if (selected_source_mining_safe or native_chain_progress_safe) and ( + str(sync_progress.get("status") or "").lower() == "syncing" + or sync_progress.get("chain_syncing") is True + or sync_progress.get("evm_chain_syncing") is True + ): + sync_progress = mark_sync_progress_mining_advisory( + sync_progress, + ( + "sync progress is advisory while native P2P freshness/current-chain proof is safe; " + "EVM/public-RPC lag alone must not pause mining" + ), + ) + sync_health["mining_advisory_sync"] = True pool_initial_download_transient = bool( pool.get("initial_download") - and pool_has_recent_paid_work + and readiness_override_safe and not pool.get("share_stall") ) source_job_health_ok_raw = source_job_health.get("ok") if isinstance(source_job_health, dict) else None source_job_health_ok = None if source_job_health_ok_raw is None else bool(source_job_health_ok_raw) selected_source_unready_reasons = selected_backend_unready_reasons(selected_source_health) selected_source_degraded = bool(selected_source_unready_reasons) - source_job_hard_degraded = bool(source_job_health_ok is False and not pool_has_recent_paid_work) + source_degradation_advisory_safe = bool(readiness_override_safe or native_chain_progress_safe) + source_degradation_advisory_basis = ( + "accepted block submission remains fresh" + if readiness_override_safe + else "native P2P freshness/current-chain proof remains safe" + ) + source_job_hard_degraded = bool(source_job_health_ok is False and not source_degradation_advisory_safe) selected_source_degradation = selected_backend_source_degradation( selected_source_degraded, - pool_has_recent_paid_work, + source_degradation_advisory_safe, ) source_selected_backend_hard_degraded = bool(selected_source_degradation["hard"]) source_selected_backend_advisory_degraded = bool(selected_source_degradation["advisory"]) @@ -5947,6 +6366,89 @@ def add_maintenance_warning(message: str) -> None: if isinstance(selected_source_health, dict) else None ) + source_ready_miners = safe_int(source_job_health.get("ready_miners"), -1) + source_ready_miners_known = "ready_miners" in source_job_health + native_template_health = ( + sync_progress.get("native_template_health") + if isinstance(sync_progress.get("native_template_health"), dict) + else {} + ) + template_coinbase_address = str( + selected_source_health.get("node_template_coinbase_address") + or native_template_health.get("template_coinbase_address") + or "" + ) + template_coinbase_valid = template_coinbase_valid_signal( + selected_source_health.get("node_template_coinbase_valid"), + str(selected_source_health.get("node_template_coinbase_address") or ""), + str(selected_source_health.get("node_reason_code") or selected_source_health.get("node_get_block_template_reason_code") or ""), + ) + if template_coinbase_valid is None: + template_coinbase_valid = template_coinbase_valid_signal( + native_template_health.get("template_coinbase_valid"), + template_coinbase_address, + str(native_template_health.get("reason_code") or native_template_health.get("get_block_template_reason_code") or ""), + ) + if template_coinbase_valid is None: + template_coinbase_valid = template_coinbase_address_valid(template_coinbase_address) + template_coinbase_rejects = pool["template_coinbase_rejects"] + zero_coinbase_rejects = zero_coinbase_reject_total(template_coinbase_rejects) + zero_coinbase_rate = update_zero_coinbase_reject_rate( + zero_coinbase_rejects, + metrics_available=pool_metrics.get("status") == "ok", + ) + local_stale_stats = local_stale_block_submit_stats( + pool_metrics.get("block_submit_outcomes") + if isinstance(pool_metrics.get("block_submit_outcomes"), Mapping) + else {} + ) + template_safety = { + "template_coinbase_valid": template_coinbase_valid, + "template_coinbase_address": template_coinbase_address, + "zero_coinbase_reject_total": _prometheus_json_number(zero_coinbase_rejects), + "zero_coinbase_reject_rate_per_second": zero_coinbase_rate.get("zero_coinbase_reject_rate_per_second"), + "zero_coinbase_reject_delta": zero_coinbase_rate.get("zero_coinbase_reject_delta"), + "zero_coinbase_reject_sample_elapsed_seconds": zero_coinbase_rate.get("sample_elapsed_seconds"), + "ready_miner_job_count": source_ready_miners if source_ready_miners_known else None, + "local_stale_ratio": local_stale_stats.get("local_stale_ratio"), + "local_stale_block_submits": local_stale_stats.get("local_stale"), + "accepted_block_submits": local_stale_stats.get("accepted"), + "automation_restart": automation_restart_marker(latest_action), + "selected_backend": pool.get("selected_backend") or "", + "source": ( + "selected-backend-node-health" + if selected_source_health.get("node_template_coinbase_valid") is not None + else ("native-template-health" if native_template_health else "pool-metrics") + ), + } + pool["template_safety"] = template_safety + sync_health["template_coinbase_valid"] = template_coinbase_valid + sync_health["zero_coinbase_reject_total"] = _prometheus_json_number(zero_coinbase_rejects) + sync_health["ready_miner_job_count"] = template_safety["ready_miner_job_count"] + sync_health["local_stale_ratio"] = template_safety["local_stale_ratio"] + if template_coinbase_valid is False: + selected_source_mining_safe = False + recent_paid_backend_safe = False + native_progress_paid_work_safe = False + readiness_override_safe = False + sync_health["selected_backend_recent_paid_work_safe"] = False + sync_health["native_progress_paid_work_safe"] = False + sync_health["readiness_override_safe"] = False + sync_health["selected_backend_mining_safe"] = False + add_sync_warning("selected mining template coinbase is invalid or zero") + source_job_no_ready_miners = bool( + connected_miners > 0 + and not pool_has_recent_paid_work + and source_ready_miners_known + and source_ready_miners <= 0 + ) + source_job_readiness_unknown = bool( + connected_miners > 0 + and not pool_has_recent_paid_work + and not source_ready_miners_known + ) + pool["source_job_no_ready_miners"] = source_job_no_ready_miners + pool["source_job_readiness_unknown"] = source_job_readiness_unknown pool_initial_download_needs_repair = bool(pool.get("initial_download") and not pool_initial_download_transient) pool["initial_download_transient"] = pool_initial_download_transient pool["initial_download_needs_repair"] = pool_initial_download_needs_repair @@ -5954,7 +6456,25 @@ def add_maintenance_warning(message: str) -> None: sync_health["pool_has_recent_share_activity"] = pool_has_recent_share_activity sync_health["pool_has_recent_paid_work"] = pool_has_recent_paid_work sync_health["pool_has_recent_mining"] = pool_has_recent_paid_work - if connected_miners > 0 and pool_has_recent_paid_work and sync_warnings: + sync_health["pool_metrics_accepted_block_submissions"] = _prometheus_json_number(metrics_accepted_block_submissions) + sync_health["pool_paid_work_state"] = paid_work_state + sync_health["selected_backend_raw_mining_safe"] = raw_selected_source_mining_safe + sync_health["selected_backend_native_p2p_current_safe"] = native_p2p_current_safe + sync_health["native_chain_progress_safe"] = native_chain_progress_safe + sync_health["selected_backend_recent_paid_work_safe"] = recent_paid_backend_safe + sync_health["native_progress_paid_work_safe"] = native_progress_paid_work_safe + sync_health["readiness_override_safe"] = readiness_override_safe + sync_health["source_degradation_advisory_safe"] = source_degradation_advisory_safe + sync_health["source_degradation_advisory_basis"] = source_degradation_advisory_basis + sync_health["selected_backend_mining_safe"] = selected_source_mining_safe + advisory_sync_warning_basis = "" + if readiness_override_safe: + advisory_sync_warning_basis = "accepted block submission remains fresh" + elif raw_selected_source_mining_safe and source_job_health_ok is not False: + advisory_sync_warning_basis = "selected backend is mineable, submit-ready, and native P2P fresh" + elif native_chain_progress_safe and not selected_source_unready_reasons and source_job_health_ok is not False: + advisory_sync_warning_basis = source_degradation_advisory_basis + if connected_miners > 0 and advisory_sync_warning_basis and sync_warnings: advisory_sync_warnings = [ item for item in sync_warnings if is_recent_mining_sync_noise(item) @@ -5969,12 +6489,13 @@ def add_maintenance_warning(message: str) -> None: if not is_recent_mining_sync_noise(item) ] for item in advisory_sync_warnings: - add_maintenance_warning(f"{item}; accepted block submission remains fresh") + add_maintenance_warning(f"{item}; {advisory_sync_warning_basis}") readiness_contract = selected_backend_readiness_contract( str(pool.get("selected_backend") or ""), selected_source_health, source_job_health, pool_has_recent_paid_work, + readiness_override_safe, ) pool["selected_backend_readiness_contract"] = readiness_contract if pool_initial_download_needs_repair: @@ -5998,7 +6519,7 @@ def add_maintenance_warning(message: str) -> None: if connected_miners > 0 and source_job_hard_degraded: add_sync_warning("pool source job health reports not-ok and accepted block submission is stale") elif connected_miners > 0 and source_job_health_ok is False: - add_maintenance_warning("pool source job health is advisory-degraded while accepted block submission remains fresh") + add_maintenance_warning(f"pool source job health is advisory-degraded while {source_degradation_advisory_basis}") if connected_miners > 0 and source_selected_backend_hard_degraded: backend = pool.get("selected_backend") or "selected backend" add_sync_warning( @@ -6008,12 +6529,18 @@ def add_maintenance_warning(message: str) -> None: elif connected_miners > 0 and source_selected_backend_advisory_degraded: backend = pool.get("selected_backend") or "selected backend" add_maintenance_warning( - f"pool source health says {backend} is degraded, but accepted block submission remains fresh " + f"pool source health says {backend} is degraded, but {source_degradation_advisory_basis} " f"({', '.join(selected_source_unready_reasons)})" ) elif connected_miners > 0 and source_health_transient_degraded: backend = pool.get("selected_backend") or "selected backend" - add_maintenance_warning(f"pool source health says {backend} is degraded, but accepted block submission remains fresh") + add_maintenance_warning(f"pool source health says {backend} is degraded, but {source_degradation_advisory_basis}") + if source_job_no_ready_miners: + add_sync_warning( + f"pool source job health reports zero ready miners while {connected_miners} miner(s) are connected" + ) + elif source_job_readiness_unknown: + add_sync_warning("pool source job readiness is unknown while miners are connected") if connected_miners > 0 and pool.get("share_stall"): age = pool.get("last_valid_share_age_seconds") age_text = f"{age}s" if age is not None else "unknown" @@ -6164,7 +6691,6 @@ def add_maintenance_warning(message: str) -> None: ["docker", "images", "--format", "{{.Repository}}:{{.Tag}} {{.Size}}"], timeout=docker_images_timeout, ).stdout.strip() - sync_progress = sync_progress_for_display_nodes(collect_sync_progress(), display_nodes) adaptive_concurrency = adaptive_worker_budgets( {**host_pressure, "chain_rpc_latency_ms": _sync_chain_rpc_latency_ms(sync_progress)} ) @@ -6220,10 +6746,26 @@ def add_maintenance_warning(message: str) -> None: sync_health["node_importing"] = bool(active_import_nodes) if active_import_nodes: sync_health["node_importing_nodes"] = active_import_nodes + catchup_ignore_remaining_blocks = bool(pool_has_recent_paid_work or native_chain_progress_safe) catchup_mining_ready = bool( - sync_progress.get("status") == "synced" - and not selected_source_unready_reasons - and source_job_health_ok is not False + ( + sync_progress.get("status") == "synced" + or selected_source_mining_safe + or pool_has_recent_paid_work + or native_chain_progress_safe + ) + and ( + pool_has_recent_paid_work + or not selected_source_unready_reasons + or readiness_override_safe + or native_chain_progress_safe + ) + and ( + pool_has_recent_paid_work + or source_job_health_ok is not False + or readiness_override_safe + or native_chain_progress_safe + ) and not pool.get("initial_download") ) catchup_policy = build_catchup_policy( @@ -6233,6 +6775,7 @@ def add_maintenance_warning(message: str) -> None: selected_source_health, host_pressure, mining_ready=catchup_mining_ready, + ignore_remaining_blocks=catchup_ignore_remaining_blocks, ) if catchup_policy.get("active"): pool_down_message = f"{POOL_CONTAINER} is not running" @@ -6370,6 +6913,7 @@ def add_maintenance_warning(message: str) -> None: "sync_coordinator": sync_coordinator, "catchup_policy": catchup_policy, "rpc_template_health": template_probe_health, + "template_safety": template_safety, "pool": pool, "pool_metrics": pool_metrics, "pool_health": pool_health, @@ -7470,10 +8014,13 @@ def node_template_health_snapshot(url: str, timeout: float) -> dict[str, Any]: def native_template_health_is_mining_safe(health: dict[str, Any]) -> bool: if not health.get("available"): return False + if health.get("template_coinbase_valid") is False: + return False if health.get("last_template_build_error_blocking") is True: return False required_true = ( "submit_ready", + "template_usable", "get_block_template_ready", "p2p_mining_fresh", "sync_allowed", @@ -7597,7 +8144,10 @@ def native_template_health_synced_progress( evm_lag: dict[str, Any], ) -> dict[str, Any]: peer_count = safe_int(health.get("p2p_fresh_consensus_peer_count"), 0) - return { + evm_chain_syncing = bool(evm_lag.get("chain_syncing")) + payload = { + **chain, + **evm_lag, "status": "synced", "percent": 100.0, "current_block": current_block, @@ -7611,14 +8161,37 @@ def native_template_health_synced_progress( "p2p_connections": peer_count, "native_template_health": compact_template_health_for_status(health), "evm_reference_lag_diagnostic_blocks": evm_lag.get("evm_lag_to_reference"), - **chain, - **evm_lag, + "native_is_current": True, + "chain_syncing": False, + "evm_chain_syncing": evm_chain_syncing, + "mining_advisory_sync": evm_chain_syncing, } + if evm_chain_syncing: + payload.setdefault( + "evm_sync_advisory", + "eth_syncing active while native P2P mining state is current", + ) + return payload def sync_progress_native_template_health_synced(progress: dict[str, Any]) -> bool: health = progress.get("native_template_health") - return isinstance(health, dict) and native_template_health_is_chain_synced(health) + if isinstance(health, dict) and native_template_health_is_chain_synced(health): + return True + if progress.get("native_is_current") is not True: + return False + if str(progress.get("status") or "").lower() not in {"synced", "ok"}: + return False + if safe_int(progress.get("remaining_blocks"), 0) > NATIVE_SYNC_LEAD_THRESHOLD: + return False + for key in ("p2p_network_gap", "peer_mainorder_gap"): + if safe_int(progress.get(key), 0) > NATIVE_SYNC_LEAD_THRESHOLD: + return False + if progress.get("chain_syncing") is True and not ( + progress.get("evm_chain_syncing") is True or progress.get("mining_advisory_sync") is True + ): + return False + return True def compact_template_health_for_status(health: dict[str, Any]) -> dict[str, Any]: @@ -7626,8 +8199,11 @@ def compact_template_health_for_status(health: dict[str, Any]) -> dict[str, Any] "available", "mineable_now", "submit_ready", + "template_usable", "get_block_template_ready", "get_block_template_reason_code", + "template_coinbase_address", + "template_coinbase_valid", "p2p_current", "p2p_mining_fresh", "p2p_mining_fresh_reason_code", @@ -7666,6 +8242,10 @@ def node_sync_progress(source: str, url: str, timeout: float = NODE_CHAIN_RPC_TI if evm_lag.get("chain_syncing") is True: sync_current = safe_int(evm_lag.get("sync_current_block"), sync_current) sync_highest = safe_int(evm_lag.get("sync_highest_block"), sync_highest) + if native_template_health_is_mining_safe(template_health) and not ( + evm_lag.get("public_chain_diverged") or evm_lag.get("solo_mining_suspected") + ): + return native_template_health_synced_progress(source, template_health, current, chain, evm_lag) if chain.get("chain_syncing") is True or evm_lag.get("chain_syncing") is True: evm_block = safe_int(evm_lag.get("evm_block_count"), None) progress_current = sync_current if sync_current is not None else evm_block if evm_block is not None else current @@ -7694,8 +8274,6 @@ def node_sync_progress(source: str, url: str, timeout: float = NODE_CHAIN_RPC_TI **chain, **evm_lag, } - if native_template_health_is_mining_safe(template_health): - return native_template_health_synced_progress(source, template_health, current, chain, evm_lag) native_template_sync = native_template_health_sync_progress(source, template_health, current) if native_template_sync: native_template_sync.update(chain) @@ -7785,7 +8363,7 @@ def collect_sync_progress() -> dict[str, Any]: starting_values = [] error = "; ".join(item.get("error", "") for item in per_node.values() if item.get("error")) - return { + aggregate = { "status": status, "percent": percent, "current_block": min(current_values) if current_values else None, @@ -7815,6 +8393,25 @@ def collect_sync_progress() -> dict[str, Any]: "error": error, "nodes": per_node, } + if known: + if all(sync_progress_native_template_health_synced(item) for item in known): + aggregate["native_is_current"] = True + evm_chain_syncing = any( + item.get("evm_chain_syncing") is True + or (item.get("chain_syncing") is True and item.get("mining_advisory_sync") is True) + for item in known + ) + if evm_chain_syncing: + aggregate["evm_chain_syncing"] = True + if status == "synced" and aggregate.get("native_is_current") is True: + aggregate["chain_syncing"] = False + aggregate["mining_advisory_sync"] = True + aggregate["evm_sync_advisory"] = "eth_syncing active while native P2P mining state is current" + elif status == "synced": + aggregate["chain_syncing"] = False + elif status == "syncing": + aggregate["chain_syncing"] = True + return aggregate def sync_progress_for_display_nodes(sync_progress: dict[str, Any], display_nodes: list[str]) -> dict[str, Any]: diff --git a/ops/pool_start_gate.py b/ops/pool_start_gate.py index 4ac861b..4bf441a 100644 --- a/ops/pool_start_gate.py +++ b/ops/pool_start_gate.py @@ -25,8 +25,9 @@ STATUS_SAMPLER_FILE = RUNTIME_DIR / "status-sampler.json" DEFAULT_STATUS_MAX_AGE_SECONDS = float(os.environ.get("BDAG_POOL_START_GATE_STATUS_MAX_AGE_SECONDS", "180")) MIN_POOL_START_PEERS = int(os.environ.get("BDAG_POOL_START_GATE_MIN_PEERS", "2")) +MAX_NATIVE_PEER_LEAD_BLOCKS = int(os.environ.get("BDAG_POOL_START_GATE_MAX_NATIVE_PEER_LEAD_BLOCKS", "12")) REQUIRE_CANONICAL_SAFETY = str( - os.environ.get("BDAG_POOL_START_GATE_REQUIRE_CANONICAL_SAFETY", "1") + os.environ.get("BDAG_POOL_START_GATE_REQUIRE_CANONICAL_SAFETY", "0") ).strip().lower() not in {"0", "false", "no", "off"} UNSAFE_MODES = {"catchup_pause", "syncing", "unknown", "waiting_for_status_sample"} READY_DOWN_MODES = {"synced", "mining", "ready_no_miners"} @@ -192,6 +193,130 @@ def canonical_safety_proven(status: dict[str, Any]) -> tuple[bool, str]: return False, f"canonical public-chain safety proof is unsafe{suffix}" +def _native_template_health_payloads(status: dict[str, Any]) -> list[tuple[str, dict[str, Any]]]: + payloads: list[tuple[str, dict[str, Any]]] = [] + + def append(scope: str, value: Any) -> None: + if isinstance(value, dict): + payloads.append((scope, value)) + + append("status", status.get("native_template_health")) + sync_progress = status.get("sync_progress") + if isinstance(sync_progress, dict): + append("sync_progress", sync_progress.get("native_template_health")) + nodes = sync_progress.get("nodes") + if isinstance(nodes, dict): + for name, node in nodes.items(): + if isinstance(node, dict): + append(f"node:{name}", node.get("native_template_health")) + nodes = status.get("nodes") + if isinstance(nodes, dict): + for name, node in nodes.items(): + if isinstance(node, dict): + append(f"node:{name}", node.get("native_template_health")) + return payloads + + +def _health_bool(payload: dict[str, Any], *keys: str) -> bool | None: + for key in keys: + value = payload.get(key) + if isinstance(value, bool): + return value + if isinstance(value, (int, float)): + return bool(value) + if isinstance(value, str): + lowered = value.strip().lower() + if lowered in {"1", "true", "yes", "on", "ready", "ok"}: + return True + if lowered in {"0", "false", "no", "off", "not_ready", "fail", "failed"}: + return False + return None + + +def native_mining_safety_proven(status: dict[str, Any]) -> tuple[bool, str]: + payloads = _native_template_health_payloads(status) + if not payloads: + return False, "native mining safety proof is missing" + + details: list[str] = [] + for scope, payload in payloads: + failures: list[str] = [] + if _health_bool(payload, "chain_current", "is_current") is not True: + failures.append("chain_current_not_true") + if _health_bool(payload, "p2p_mining_fresh") is not True: + reason = str(payload.get("p2p_mining_fresh_reason_code") or "unknown") + failures.append(f"p2p_mining_fresh_not_true:{reason}") + if _health_bool(payload, "get_block_template_ready") is not True: + failures.append("get_block_template_ready_not_true") + if _health_bool(payload, "submit_ready") is not True: + failures.append("submit_ready_not_true") + if _health_bool(payload, "mineable_now") is False: + failures.append("mineable_now_false") + if _health_bool(payload, "template_usable") is False: + failures.append("template_usable_false") + if _health_bool(payload, "sync_allowed") is not True: + failures.append("sync_allowed_not_true") + + fresh_peers = _safe_int(payload.get("p2p_fresh_consensus_peer_count")) + if fresh_peers is None: + failures.append("fresh_consensus_peer_count_unknown") + elif fresh_peers < MIN_POOL_START_PEERS: + failures.append(f"fresh_consensus_peer_count_{fresh_peers}_lt_{MIN_POOL_START_PEERS}") + + peer_lead = _safe_int(payload.get("p2p_best_peer_lead_blocks")) + if peer_lead is None: + peer_lead = _safe_int(payload.get("p2p_peer_height_lead")) + if peer_lead is None: + failures.append("p2p_peer_lead_unknown") + elif peer_lead > MAX_NATIVE_PEER_LEAD_BLOCKS: + failures.append(f"p2p_peer_lead_{peer_lead}_gt_{MAX_NATIVE_PEER_LEAD_BLOCKS}") + + if not failures: + return True, f"native mining safety proof accepted from {scope}" + details.append(f"{scope}: {', '.join(failures[:6])}") + + return False, "native mining safety proof is unsafe: " + "; ".join(details[:4]) + + +def native_safe_advisory_sync(status: dict[str, Any], native_safe: bool) -> bool: + if not native_safe: + return False + sync_health = status.get("sync_health") if isinstance(status.get("sync_health"), dict) else {} + catchup_policy = status.get("catchup_policy") if isinstance(status.get("catchup_policy"), dict) else {} + if any( + sync_health.get(key) is True + for key in ( + "mining_advisory_sync", + "native_chain_progress_safe", + "selected_backend_native_p2p_current_safe", + "selected_backend_mining_safe", + ) + ): + return True + if catchup_policy.get("remaining_blocks_advisory") is True or catchup_policy.get("native_advisory_safe") is True: + return True + + def progress_is_advisory(progress: Any) -> bool: + if not isinstance(progress, dict): + return False + return bool( + progress.get("mining_advisory_sync") is True + or ( + progress.get("evm_chain_syncing") is True + and progress.get("chain_syncing") is not True + ) + ) + + sync_progress = status.get("sync_progress") + if progress_is_advisory(sync_progress): + return True + if isinstance(sync_progress, dict): + nodes = sync_progress.get("nodes") + if isinstance(nodes, dict) and any(progress_is_advisory(node) for node in nodes.values()): + return True + return False + + def pool_start_decision(status: dict[str, Any] | None, *, status_source: str = "direct") -> PoolStartGateDecision: if not isinstance(status, dict): return PoolStartGateDecision(False, ("stack status unavailable; cannot prove pool start is safe",), status_source) @@ -212,15 +337,18 @@ def pool_start_decision(status: dict[str, Any] | None, *, status_source: str = " sync_health = status.get("sync_health") if isinstance(status.get("sync_health"), dict) else {} catchup_policy = status.get("catchup_policy") if isinstance(status.get("catchup_policy"), dict) else {} + native_safe, native_reason = native_mining_safety_proven(status) + advisory_native_sync = native_safe_advisory_sync(status, native_safe) + if sync_health.get("public_chain_divergence") or sync_health.get("public_chain_divergence_nodes"): reasons.append("public-chain divergence containment is active") - if catchup_policy.get("active") or sync_health.get("catchup_pause_active"): + if (catchup_policy.get("active") or sync_health.get("catchup_pause_active")) and not advisory_native_sync: reasons.append("chain catch-up pause is active") sync_progress = status.get("sync_progress") if isinstance(status.get("sync_progress"), dict) else {} sync_status = str(sync_progress.get("status") or "").strip().lower() sync_lag = _sync_progress_lag_blocks(status) - if sync_status in {"syncing", "catchup_pause"}: + if sync_status in {"syncing", "catchup_pause"} and not advisory_native_sync: if sync_lag > 0: reasons.append(f"sync progress is {sync_status} with {sync_lag} block(s) remaining") else: @@ -235,9 +363,9 @@ def pool_start_decision(status: dict[str, Any] | None, *, status_source: str = " mode = str(status.get("mode") or "").strip().lower() overall = str(status.get("overall") or "").strip().lower() - if mode in UNSAFE_MODES: + if mode in UNSAFE_MODES and not advisory_native_sync: reasons.append(f"status mode is not safe for pool start: {mode}") - if overall == "syncing": + if overall == "syncing" and not advisory_native_sync: reasons.append("overall stack status is syncing") if overall == "down" and mode not in READY_DOWN_MODES: reasons.append(f"overall stack status is down with non-ready mode: {mode or 'unknown'}") @@ -246,6 +374,9 @@ def pool_start_decision(status: dict[str, Any] | None, *, status_source: str = " if isinstance(rpc_template, dict) and rpc_template.get("all_nodes_ready") is False: reasons.append("node template health is not ready") + if not native_safe: + reasons.append(native_reason) + if REQUIRE_CANONICAL_SAFETY: safe, canonical_reason = canonical_safety_proven(status) if not safe: @@ -263,6 +394,8 @@ def pool_start_decision(status: dict[str, Any] | None, *, status_source: str = " ("client in initial download", "node reports initial download"), ) for needle, message in text_blockers: + if advisory_native_sync and needle != "public-chain divergence": + continue if needle in reason_text and message not in reasons: reasons.append(message) diff --git a/ops/portable.env.example b/ops/portable.env.example index b8d2638..c2eed47 100644 --- a/ops/portable.env.example +++ b/ops/portable.env.example @@ -21,6 +21,7 @@ BDAG_DASHBOARD_EARNINGS_SAMPLER_INTERVAL_SECONDS=60 BDAG_DASHBOARD_GLOBAL_SAMPLER_INTERVAL_SECONDS=60 BDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS=120 BDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120 +BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS=300 BDAG_CATCHUP_PAUSE_ENABLED=1 BDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300 BDAG_CATCHUP_IO_PRESSURE_PAUSE_ENABLED=1 @@ -88,9 +89,11 @@ BDAG_NODE_RPC_URLS=node=http://127.0.0.1:38131 BDAG_GLOBAL_CHAIN_RPC_URLS=node=http://127.0.0.1:38131 NODE_RPC_URLS=http://127.0.0.1:38131 POOL_SUBMIT_RPC_URLS= -POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=true +POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30 POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com +POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5 +POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60 # BDAG_PUBLIC_RPC_URLS=engineering=https://rpc.blockdag.engineering,bdagscan=https://rpc.bdagscan.com # BDAG_EXPLORER_API_URLS=bdagscan=https://bdagscan.com # BDAG_BLOCKSCOUT_V2_URLS=engineering=https://explorer.blockdag.engineering diff --git a/ops/release-install.sh b/ops/release-install.sh index 580ce05..07b6002 100755 --- a/ops/release-install.sh +++ b/ops/release-install.sh @@ -241,13 +241,30 @@ random_secret() { set_env_value() { local file="$1" key="$2" value="$3" + local line escaped + line="${key}=$(quote_env_value "$value")" + escaped="$(printf '%s' "$line" | sed 's/[&|\\]/\\&/g')" if grep -q "^${key}=" "$file"; then - sed -i "s|^${key}=.*|${key}=${value}|" "$file" + sed -i "s|^${key}=.*|${escaped}|" "$file" else - printf '%s=%s\n' "$key" "$value" >> "$file" + printf '%s\n' "$line" >> "$file" fi } +quote_env_value() { + local value="$1" + case "$value" in + *[!A-Za-z0-9_./:@,%+=-]*) + printf '"' + printf '%s' "$value" | sed 's/[\\$"`]/\\&/g' + printf '"' + ;; + *) + printf '%s' "$value" + ;; + esac +} + set_stack_default_env_value() { local file="$1" key="$2" fallback="${3:-}" set_env_value "$file" "$key" "$(stack_default "$key" "$fallback")" diff --git a/ops/savepoints/20260621-202754-good-mining-state.json b/ops/savepoints/20260621-202754-good-mining-state.json new file mode 100644 index 0000000..3d52a7b --- /dev/null +++ b/ops/savepoints/20260621-202754-good-mining-state.json @@ -0,0 +1,158 @@ +{ + "schema_version": 1, + "savepoint_id": "20260621-202754-stack-redis-good-mining-state", + "recorded_at": "2026-06-21T20:27:54+02:00", + "host": "jeremy", + "timezone": "Africa/Johannesburg", + "purpose": "Non-regression save point for the current good stack-redis mining state. Future changes must preserve or measurably improve this state before being treated as progress.", + "source_commits": { + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "commit": "c305b662db2aaf4241c6705721408b4445a017c1" + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "commit": "e02f5b5d68ca62888cbd145fd6928e04c98d3555" + }, + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "commit": "33a449269607790e42bc38bcfa3fd213d82ed02e" + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "commit": "8a28b1817f3e10fe1505be5ba1dab93913cb6400" + } + }, + "runtime_images": { + "stack-node": "sha256:229ad85f2eaa4427daf5bd554cf8ddc4279b5af97a83e70e0fa1e96c2ce2e8f7", + "stack-pool": "sha256:10636aca234fde99d09e32631cfd2c426f6135de0e9c3fb66cb5c35d2d642c10", + "stack-dashboard": "sha256:5cbcb0623ff97b7eb3fda55e84e16074c2e980e3185b819e8544ce0b1645370a", + "stack-status-sampler": "sha256:f5b2d0284e3f8a7fc25a73e7badd30f1e137043dd9bb79c3d3e57cc748837b50", + "stack-watchdog": "sha256:77117da6e9d9f9dcaee8a104dfe109db20db446f9ca96308f4a447886e1fd514", + "stack-sentinel": "sha256:af5e5fb015d4bf408dd6bddb6691cebff017486f6a6734baf7373647cbdcb53b" + }, + "live_evidence": { + "dashboard_status": { + "status": "ok", + "overall": "ok", + "mode": "mining", + "can_accept_shares": true, + "can_submit_blocks": true, + "chain_block_count": 12183089, + "miner_health": { + "managed": 4, + "connected": 4, + "configured": 4 + }, + "asic_performance": { + "lanes": 4, + "ready": 4, + "accepted_30m": 564, + "rejected_local_30m": 43 + } + }, + "node_template_health": { + "checked_at_unix_ms": 1782066456306, + "main_order": 12183088, + "mineable_now": true, + "submit_ready": true, + "get_block_template_ready": true, + "sync_reason_code": "ok", + "p2p_mining_fresh": true, + "p2p_consensus_peer_count": 3, + "p2p_fresh_consensus_peer_count": 3, + "p2p_best_peer_lead_blocks": -2, + "p2p_best_peer_graph_state_age_ms": 756, + "chain_current": true + }, + "pool_metrics": { + "active_connections": 4, + "ready_miners": 4, + "block_submit_outcomes": { + "accepted_ok": 566, + "rejected_node_syncing": 5, + "rejected_local_duplicate_block": 1, + "rejected_local_old_template_sequence": 1, + "rejected_local_stale_job": 39, + "rejected_local_stale_parent": 2 + }, + "template_coinbase_rejects": { + "ws_push_zero_template_address": 1162 + } + }, + "asic_performance": { + "generated_at_unix_ms": 1782066456237, + "lanes": 4, + "ready": 4, + "accepted_30m": 566, + "rejected_local_30m": 43, + "lanes_detail": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "remote_host": "192.168.1.101", + "ready": true, + "health": "ok", + "current_job_age_ms": 252, + "blocks_30m": 138, + "rejected_local_30m": 10 + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "remote_host": "192.168.1.14", + "ready": true, + "health": "ok", + "current_job_age_ms": 252, + "blocks_30m": 143, + "rejected_local_30m": 8 + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "remote_host": "192.168.1.105", + "ready": true, + "health": "ok", + "current_job_age_ms": 252, + "blocks_30m": 136, + "rejected_local_30m": 12 + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "remote_host": "192.168.1.102", + "ready": true, + "health": "ok", + "current_job_age_ms": 252, + "blocks_30m": 149, + "rejected_local_30m": 13 + } + ] + } + }, + "non_regression_contract": { + "must_preserve": [ + "dashboard mode must remain mining when node and pool are healthy", + "dashboard can_accept_shares and can_submit_blocks must remain true in healthy state", + "native node getTemplateHealth must report mineable_now, submit_ready, get_block_template_ready, p2p_mining_fresh, and chain_current", + "native node must have at least two fresh consensus peers before the pool is treated as mining-safe", + "pool must keep all four ASIC lanes identified by asic_mac, connected, ready, and health=ok", + "pool must reject missing, zero, invalid, or mismatched coinbase templates before miner broadcast", + "dashboard status must expose ASIC performance rows for all four local X100 miners" + ], + "minimum_live_gates": { + "dashboard_can_accept_shares": true, + "dashboard_can_submit_blocks": true, + "node_p2p_fresh_consensus_peer_count": 2, + "pool_active_connections": 4, + "pool_ready_miners": 4, + "asic_ready_lanes": 4 + }, + "performance_baseline": { + "sample_window_minutes": 30, + "accepted_blocks_total": 566, + "expected_asic_count": 4, + "note": "Short-window production is stochastic. Do not lower the operational baseline without a longer comparison window and evidence that the new state increases paid on-chain blocks." + } + }, + "known_follow_up": { + "issue": "pool_template_coinbase_rejects_total continues increasing for source=ws-push and reason=zero-template-address", + "policy": "This is a source-quality issue to fix upstream. Do not weaken the pool coinbase invariant or allow zero-coinbase templates to reach ASIC work." + } +} diff --git a/ops/savepoints/20260621-233309-tonight-baseline-retained.json b/ops/savepoints/20260621-233309-tonight-baseline-retained.json new file mode 100644 index 0000000..1b868f6 --- /dev/null +++ b/ops/savepoints/20260621-233309-tonight-baseline-retained.json @@ -0,0 +1,1788 @@ +{ + "schema_version": 1, + "savepoint_id": "20260621-233309-tonight-baseline-retained", + "recorded_at": "2026-06-21T23:33:09.259111+02:00", + "host": "asus-miner", + "timezone": "Africa/Johannesburg", + "purpose": "Retained recovered-good baseline before overnight upgrade/design mission. Do not delete until Jeremy explicitly releases it.", + "github_rule": "Only authenticated gh CLI/API may be used for GitHub operations; no raw GitHub git pull/fetch/push.", + "memory_status": "## main...origin/main [ahead 5, behind 5]", + "source_commits": { + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "commit": "7b3c812d476bd4d3ef96a9b449de1d692330e40b", + "status_short": "", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-stack: codex post-savepoint zero-coinbase/watchdog patchset 20260621" + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "commit": "e02f5b5d68ca62888cbd145fd6928e04c98d3555", + "status_short": "", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-pool: codex post-savepoint zero-coinbase pool patchset 20260621" + }, + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "commit": "33a449269607790e42bc38bcfa3fd213d82ed02e", + "status_short": "", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-corechain: codex post-savepoint zero-coinbase corechain patchset 20260621" + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "commit": "8a28b1817f3e10fe1505be5ba1dab93913cb6400", + "status_short": "", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-dashboard: codex post-savepoint dashboard safety patchset 20260621" + } + }, + "rendered_compose_sha256": "332b852c8f8b2f2f98f084dfbcc1cf968f63abb9f401e874af3da73d84b8b39f", + "rendered_compose_redacted": { + "name": "stack", + "networks": { + "default": { + "name": "stack_default", + "ipam": {} + } + }, + "services": { + "dashboard": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "additional_contexts": { + "blockdag_corechain": "/home/jeremy/blockdag-source/blockdag-corechain", + "cpu_miner_src": "/home/jeremy/blockdag-source/stack-redis", + "dashboard_src": "/home/jeremy/blockdag-source/redis-dash", + "pool_src": "/home/jeremy/blockdag-source/pool", + "stack_src": "/home/jeremy/blockdag-source/stack-redis" + }, + "target": "dashboard" + }, + "cpu_shares": 256, + "command": null, + "container_name": "dashboard", + "depends_on": { + "node": { + "condition": "service_started", + "required": true + }, + "pool": { + "condition": "service_started", + "required": true + } + }, + "entrypoint": null, + "environment": { + "ADDR": "0.0.0.0:8088", + "BDAG_ASIC_LAN_CIDRS": "192.168.1.0/24", + "BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS": "3", + "BDAG_CHAIN_POLL_SECONDS": "2", + "BDAG_CHAIN_WS_READ_LIMIT_BYTES": "1048576", + "BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS": "10", + "BDAG_DASHBOARD_HEALTH_STALE_SECONDS": "20", + "BDAG_DASHBOARD_REDIS_ENABLED": "1", + "BDAG_DASHBOARD_REDIS_MANAGED": "1", + "BDAG_EVM_HTTP_URL": "http://host.docker.internal:18545", + "BDAG_EVM_SYNC_BACKOFF_SECONDS": "60", + "BDAG_EVM_WS_URL": "ws://host.docker.internal:18546", + "BDAG_GLOBAL_BLOCK_WINDOW": "600", + "BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS": "30", + "BDAG_GLOBAL_FAST_WARMUP_BLOCKS": "120", + "BDAG_GLOBAL_MAX_INCREMENTAL_GAP": "120", + "BDAG_GLOBAL_RPC_CHUNK_SIZE": "64", + "BDAG_GLOBAL_RPC_WORKERS": "4", + "BDAG_GLOBAL_SETTLE_DELAY_SECONDS": "5", + "BDAG_MINER_CONTROL_ENABLED": "0", + "BDAG_NODE_RPC_URL": "http://host.docker.internal:38131", + "BDAG_POOL_METRICS_URL": "http://host.docker.internal:9090/metrics", + "BDAG_POOL_URL": "stratum+tcp://192.168.1.100:3334", + "BDAG_REDIS_APPENDFSYNC": "everysec", + "BDAG_REDIS_APPENDONLY": "yes", + "BDAG_REDIS_CONFIG": "/etc/redis/redis.conf", + "BDAG_REDIS_MAXMEMORY": "256mb", + "BDAG_REDIS_MAXMEMORY_POLICY": "noeviction", + "BDAG_REDIS_SOCKET": "/run/dashboard-redis/redis.sock", + "BDAG_TREND_BACKFILL_CHUNK_SIZE": "128", + "BDAG_TREND_BACKFILL_DAYS": "30", + "BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS": "", + "BDAG_TREND_BACKFILL_SLEEP_MS": "100", + "BDAG_TREND_CATCHUP_INTERVAL_SECONDS": "30", + "BDAG_TREND_HISTORY_ENABLED": "1", + "BDAG_TREND_RECENT_WARMUP_BLOCKS": "3600", + "BDAG_TREND_RPC_WORKERS": "4", + "BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS": "2048", + "BDAG_TREND_SPARSE_SAMPLES_PER_PASS": "", + "DASHBOARD_TIMEZONE": "Africa/Johannesburg", + "MINING_POOL_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_RPC_PASS": "", + "NODE_RPC_USER": "test", + "POOL_ASIC_ARP_TABLE_PATH": "/host/proc/net/arp", + "POOL_ASIC_MAC_ALLOWLIST": "2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "POOL_ASIC_MAC_OVERRIDES": "192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_COINBASE_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "TEMP": "/tmp", + "TMP": "/tmp", + "TMPDIR": "/tmp" + }, + "extra_hosts": [ + "host.docker.internal=host-gateway" + ], + "healthcheck": { + "test": [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:8088/ >/dev/null" + ], + "timeout": "5s", + "interval": "10s", + "retries": 12 + }, + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "networks": { + "default": null + }, + "oom_score_adj": 300, + "platform": "linux/amd64", + "ports": [ + { + "mode": "ingress", + "host_ip": "127.0.0.1", + "target": 8088, + "published": "8088", + "protocol": "tcp" + } + ], + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777", + "/var/tmp:size=128m,mode=1777", + "/run/dashboard-redis:size=16m,mode=0700" + ], + "volumes": [ + { + "type": "volume", + "source": "dashboard-redis", + "target": "/var/lib/dashboard-redis", + "volume": {} + }, + { + "type": "bind", + "source": "/proc/net/arp", + "target": "/host/proc/net/arp", + "read_only": true, + "bind": { + "create_host_path": true + } + } + ] + }, + "miner-route": { + "cap_add": [ + "NET_ADMIN" + ], + "command": null, + "container_name": "miner-route", + "entrypoint": [ + "/bin/sh", + "-c", + "while true; do if [ -n \"$$BDAG_MINER_ROUTE_SUBNET\" ] && [ -n \"$$BDAG_MINER_ROUTE_GATEWAY\" ]; then cmd=\"ip route replace $$BDAG_MINER_ROUTE_SUBNET via $$BDAG_MINER_ROUTE_GATEWAY\"; [ -n \"$$BDAG_MINER_ROUTE_DEV\" ] && cmd=\"$$cmd dev $$BDAG_MINER_ROUTE_DEV\"; $$cmd || echo \"miner-route: failed: $$cmd\"; fi; sleep 60; done" + ], + "environment": { + "BDAG_MINER_ROUTE_DEV": "", + "BDAG_MINER_ROUTE_GATEWAY": "", + "BDAG_MINER_ROUTE_SUBNET": "" + }, + "image": "alpine:3.20", + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "network_mode": "host", + "restart": "unless-stopped" + }, + "node": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "args": { + "SNAPSHOT_PATH": "docker/no-snapshot.marker" + }, + "additional_contexts": { + "blockdag_corechain": "/home/jeremy/blockdag-source/blockdag-corechain", + "cpu_miner_src": "/home/jeremy/blockdag-source/stack-redis", + "dashboard_src": "/home/jeremy/blockdag-source/redis-dash", + "pool_src": "/home/jeremy/blockdag-source/pool", + "stack_src": "/home/jeremy/blockdag-source/stack-redis" + }, + "target": "node" + }, + "cpu_shares": 4096, + "command": null, + "container_name": "node", + "entrypoint": null, + "environment": { + "BDAG_ASIC_LAN_CIDRS": "192.168.1.0/24", + "BDAG_ASIC_LAN_INTERFACE": "enxc8a362eb4bdc", + "BDAG_DETECTED_NETWORK_TOPOLOGY": "", + "BDAG_ENTRYPOINT_CHOWN_MODE": "needed", + "BDAG_EPHEMERAL_DIR": "/run/bdag-ephemeral", + "BDAG_NETWORK": "", + "BDAG_NETWORK_TOPOLOGY": "auto", + "BDAG_NODEWORKER_LIVENESS_TIMEOUT": "5m", + "BDAG_NODE_ARCHIVAL": "0", + "BDAG_NODE_DEBUG_LEVEL": "warn", + "BDAG_NODE_MINING_NO_PENDING_TX": "1", + "BDAG_NODE_NO_FILE_LOGGING": "1", + "BDAG_NODE_OBSOLETE_HEIGHT": "20", + "BDAG_P2P_ADVERTISE_IP": "", + "BDAG_P2P_INTERFACE": "", + "BDAG_SNAPSHOT_URL": "", + "BDAG_STORAGE_PROFILE": "auto", + "BOOTSTRAP_PEER_ADDRESSES": "/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP", + "NODE_ARGS_APPEND": "--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "P2P_PORT": "8150", + "SYNC_SOURCE_NODE": "0", + "TEMP": "/tmp", + "TMP": "/tmp", + "TMPDIR": "/tmp" + }, + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "network_mode": "host", + "oom_score_adj": -900, + "platform": "linux/amd64", + "restart": "unless-stopped", + "shm_size": "536870912", + "tmpfs": [ + "/tmp:size=512m,mode=1777", + "/var/tmp:size=512m,mode=1777", + "/run/bdag-ephemeral:size=512m,mode=1777" + ], + "volumes": [ + { + "type": "bind", + "source": "/home/jeremy/blockdag-source/stack/data/node", + "target": "/var/lib/bdagStack/node", + "bind": { + "create_host_path": true + } + }, + { + "type": "volume", + "source": "nodeworker-data", + "target": "/var/lib/bdagStack/nodeworker", + "volume": {} + }, + { + "type": "bind", + "source": "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker", + "target": "/snapshot/latest.bdsnap", + "read_only": true, + "bind": { + "create_host_path": true + } + }, + { + "type": "bind", + "source": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "target": "/etc/bdagStack/node.conf", + "read_only": true, + "bind": { + "create_host_path": true + } + } + ] + }, + "pool": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "args": { + "SNAPSHOT_PATH": "docker/no-snapshot.marker" + }, + "additional_contexts": { + "blockdag_corechain": "/home/jeremy/blockdag-source/blockdag-corechain", + "cpu_miner_src": "/home/jeremy/blockdag-source/stack-redis", + "dashboard_src": "/home/jeremy/blockdag-source/redis-dash", + "pool_src": "/home/jeremy/blockdag-source/pool", + "stack_src": "/home/jeremy/blockdag-source/stack-redis" + }, + "target": "pool" + }, + "cpu_shares": 3072, + "command": null, + "container_name": "pool", + "depends_on": { + "pool-db": { + "condition": "service_healthy", + "required": true + } + }, + "entrypoint": null, + "environment": { + "BDAG_ASIC_LAN_CIDRS": "192.168.1.0/24", + "METRICS_ADDR": "0.0.0.0:9090", + "MINING_POOL_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_RPC_PASS": "", + "NODE_RPC_URL": "http://127.0.0.1:38131", + "NODE_RPC_URLS": "http://127.0.0.1:38131", + "NODE_RPC_USER": "test", + "PG_URL": "", + "POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB": "true", + "POOL_ASIC_MAC_ALLOWLIST": "2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "POOL_ASIC_MAC_OVERRIDES": "192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE": "true", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS": "8000", + "POOL_BIND_ADDR": "192.168.1.100:3334", + "POOL_BLOCK_MATURITY": "10", + "POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS": "20", + "POOL_BLOCK_TIMING_CONTROLLER_ENABLED": "true", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS": "1750", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS": "100", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES": "20", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS": "1750", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS": "100", + "POOL_BLOCK_TIMING_CONTROLLER_STEP_MS": "250", + "POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT": "5", + "POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS": "60", + "POOL_COINBASE_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS": "60", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD": "3", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS": "120", + "POOL_FEE_PERCENTAGE": "1.0", + "POOL_INSTANCE_ID": "", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS": "1750", + "POOL_PAYOUT_MATURITY": "4096", + "POOL_PAYOUT_MAX_BLOCKS_PER_TICK": "5", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED": "false", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS": "0", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED": "true", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS": "20", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS": "1000", + "POOL_PRIVATE_KEY": "", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS": "1750", + "POOL_RPC_BACKENDS": "", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED": "false", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS": "30", + "POOL_RPC_ROUTER_EVM_REFERENCE_URLS": "https://rpc.blockdag.engineering,https://rpc.bdagscan.com", + "POOL_RPC_ROUTER_NODE_HEALTH_ENABLED": "true", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS": "15", + "POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS": "30", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS": "2", + "POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS": "15", + "POOL_RUNTIME_ADMIN_ENABLED": "false", + "POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS": "60", + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD": "0", + "POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS": "120", + "POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD": "1", + "POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS": "5", + "POOL_STALE_RACE_REJECT_WINDOW_SECONDS": "10", + "POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS": "60", + "POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED": "true", + "POOL_SUBMIT_RPC_URLS": "", + "POOL_SUBMIT_STALE_BLOCK_CANDIDATES": "false", + "POOL_TEMPLATE_TTL_REFRESH_MS": "100", + "PPLNS_N_WORK": "1000", + "TEMP": "/tmp", + "TMP": "/tmp", + "TMPDIR": "/tmp", + "WALLET_RPC_URL": "http://127.0.0.1:18545" + }, + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "network_mode": "host", + "oom_score_adj": -800, + "platform": "linux/amd64", + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777", + "/var/tmp:size=128m,mode=1777" + ] + }, + "pool-db": { + "cpu_shares": 3072, + "command": null, + "container_name": "postgres", + "entrypoint": null, + "environment": { + "POSTGRES_DB": "bdagpool", + "POSTGRES_PASSWORD": "", + "POSTGRES_USER": "bdag_pool", + "TEMP": "/tmp", + "TMP": "/tmp", + "TMPDIR": "/tmp" + }, + "healthcheck": { + "test": [ + "CMD-SHELL", + "pg_isready -U bdag_pool -d bdagpool" + ], + "timeout": "5s", + "interval": "5s", + "retries": 20 + }, + "image": "postgres:15-bookworm", + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "networks": { + "default": null + }, + "oom_score_adj": -800, + "ports": [ + { + "mode": "ingress", + "host_ip": "127.0.0.1", + "target": 5432, + "published": "55432", + "protocol": "tcp" + } + ], + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777", + "/var/tmp:size=128m,mode=1777" + ], + "volumes": [ + { + "type": "volume", + "source": "postgres-data", + "target": "/var/lib/postgresql/data", + "volume": {} + }, + { + "type": "bind", + "source": "/home/jeremy/blockdag-source/stack-redis/sql/pool-schema.sql", + "target": "/docker-entrypoint-initdb.d/01-pool-schema.sql", + "read_only": true, + "bind": { + "create_host_path": true + } + } + ] + }, + "sentinel": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "target": "sentinel" + }, + "cpu_shares": 256, + "command": null, + "container_name": "sentinel", + "entrypoint": [ + "/bin/sh", + "-c", + "python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep $${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done" + ], + "environment": { + "BDAG_NODE_SERVICES": "node", + "BDAG_POOL_CONTAINER": "pool", + "BDAG_POOL_CONTAINERS": "pool", + "BDAG_POOL_DB_CONTAINER": "postgres", + "BDAG_POOL_ENV_FILE": "/home/jeremy/.env", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST": "host.docker.internal", + "BDAG_PROJECT_ROOT": "/home/jeremy", + "BDAG_RUNTIME_DIR": "/home/jeremy/ops/runtime", + "BDAG_SENTINEL_INTERVAL_SECONDS": "60", + "BDAG_SENTINEL_STATUS_URL": "http://dashboard:8088/api/status", + "BDAG_SENTINEL_USER_SERVICES": "", + "BDAG_SENTINEL_USER_TIMERS": "", + "BDAG_STACK_SERVICES": "postgres,node,pool", + "COMPOSE_PROJECT_NAME": "stack", + "NODE_RPC_PASS": "", + "NODE_RPC_USER": "test", + "TMPDIR": "/tmp" + }, + "extra_hosts": [ + "host.docker.internal=host-gateway" + ], + "group_add": [ + "115" + ], + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "networks": { + "default": null + }, + "oom_score_adj": 300, + "platform": "linux/amd64", + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777" + ], + "user": "1000:1000", + "volumes": [ + { + "type": "bind", + "source": "/var/run/docker.sock", + "target": "/var/run/docker.sock", + "bind": { + "create_host_path": true + } + }, + { + "type": "bind", + "source": "/home/jeremy", + "target": "/home/jeremy", + "bind": { + "create_host_path": true + } + } + ], + "working_dir": "/home/jeremy" + }, + "status-sampler": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "target": "status-sampler" + }, + "blkio_config": { + "weight": 100 + }, + "cpu_shares": 256, + "command": null, + "container_name": "status-sampler", + "entrypoint": [ + "/bin/sh", + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "environment": { + "BDAG_CHAIN_STATE_SELF_HEAL_COMMAND": "ops/chain-state-self-heal.sh --from-systemd", + "BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL": "http://dashboard:8088/api/status", + "BDAG_CHAIN_STATE_SELF_HEAL_UNIT": "", + "BDAG_MINING_IMPERATIVE_GUARD_UNITS": "", + "BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS": "30", + "BDAG_NODE_RPC_URLS": "node=http://host.docker.internal:38131", + "BDAG_NODE_SERVICES": "node", + "BDAG_POOL_CONTAINER": "pool", + "BDAG_POOL_CONTAINERS": "pool", + "BDAG_POOL_DB_CONTAINER": "postgres", + "BDAG_POOL_ENV_FILE": "/home/jeremy/.env", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST": "host.docker.internal", + "BDAG_PROJECT_ROOT": "/home/jeremy", + "BDAG_RUNTIME_DIR": "/home/jeremy/ops/runtime", + "BDAG_STACK_SERVICES": "postgres,node,pool", + "BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS": "60", + "BDAG_STATUS_SAMPLER_FILE": "/home/jeremy/ops/runtime/status-sampler.json", + "BDAG_STATUS_SAMPLER_INTERVAL_SECONDS": "10", + "COMPOSE_PROJECT_NAME": "stack", + "NODE_RPC_PASS": "", + "NODE_RPC_USER": "test", + "TMPDIR": "/tmp" + }, + "extra_hosts": [ + "host.docker.internal=host-gateway" + ], + "group_add": [ + "115" + ], + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "networks": { + "default": null + }, + "oom_score_adj": 300, + "platform": "linux/amd64", + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777" + ], + "user": "1000:1000", + "volumes": [ + { + "type": "bind", + "source": "/var/run/docker.sock", + "target": "/var/run/docker.sock", + "bind": { + "create_host_path": true + } + }, + { + "type": "bind", + "source": "/home/jeremy", + "target": "/home/jeremy", + "bind": { + "create_host_path": true + } + } + ], + "working_dir": "/home/jeremy" + }, + "watchdog": { + "build": { + "context": "/home/jeremy/blockdag-source/stack-redis", + "dockerfile": "dockerfile-dev", + "target": "watchdog" + }, + "cpu_shares": 256, + "command": null, + "container_name": "watchdog", + "entrypoint": [ + "/bin/sh", + "-c", + "python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop" + ], + "environment": { + "BDAG_NODE_SERVICES": "node", + "BDAG_POOL_CONTAINER": "pool", + "BDAG_POOL_CONTAINERS": "pool", + "BDAG_POOL_DB_CONTAINER": "postgres", + "BDAG_POOL_ENV_FILE": "/home/jeremy/.env", + "BDAG_POOL_HOST": "192.168.1.100", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST": "host.docker.internal", + "BDAG_PROJECT_ROOT": "/home/jeremy", + "BDAG_RUNTIME_DIR": "/home/jeremy/ops/runtime", + "BDAG_STACK_SERVICES": "postgres,node,pool", + "BDAG_STATUS_SOURCE_URL": "http://dashboard:8088/api/status", + "BDAG_WATCHDOG_INTERVAL": "60", + "BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS": "300", + "COMPOSE_PROJECT_NAME": "stack", + "NODE_RPC_PASS": "", + "NODE_RPC_USER": "test", + "TMPDIR": "/tmp" + }, + "extra_hosts": [ + "host.docker.internal=host-gateway" + ], + "group_add": [ + "115" + ], + "logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "networks": { + "default": null + }, + "oom_score_adj": 300, + "platform": "linux/amd64", + "restart": "unless-stopped", + "tmpfs": [ + "/tmp:size=128m,mode=1777" + ], + "user": "1000:1000", + "volumes": [ + { + "type": "bind", + "source": "/var/run/docker.sock", + "target": "/var/run/docker.sock", + "bind": { + "create_host_path": true + } + }, + { + "type": "bind", + "source": "/home/jeremy", + "target": "/home/jeremy", + "bind": { + "create_host_path": true + } + } + ], + "working_dir": "/home/jeremy" + } + }, + "volumes": { + "dashboard-redis": { + "name": "stack_dashboard-redis" + }, + "nodeworker-data": { + "name": "stack_nodeworker-data" + }, + "postgres-data": { + "name": "stack_postgres-data" + } + }, + "x-build-args": { + "SNAPSHOT_PATH": "docker/no-snapshot.marker" + }, + "x-dev-source-contexts": { + "blockdag_corechain": "/home/jeremy/blockdag-source/blockdag-corechain", + "cpu_miner_src": "/home/jeremy/blockdag-source/stack-redis", + "dashboard_src": "/home/jeremy/blockdag-source/redis-dash", + "pool_src": "/home/jeremy/blockdag-source/pool", + "stack_src": "/home/jeremy/blockdag-source/stack-redis" + }, + "x-mining-logging": { + "driver": "local", + "options": { + "max-file": "2", + "max-size": "10m" + } + }, + "x-node-critical": { + "blkio_config": { + "weight": 1000 + }, + "cpu_shares": 4096, + "oom_score_adj": -900, + "shm_size": "512m", + "tmpfs": [ + "/tmp:size=512m,mode=1777", + "/var/tmp:size=512m,mode=1777", + "/run/bdag-ephemeral:size=512m,mode=1777" + ] + }, + "x-ops-low": { + "blkio_config": { + "weight": 100 + }, + "cpu_shares": 256, + "oom_score_adj": 300 + }, + "x-pool-critical": { + "blkio_config": { + "weight": 900 + }, + "cpu_shares": 3072, + "oom_score_adj": -800 + } + }, + "live_containers": { + "node": { + "present": true, + "id": "cb4367844b7a81c08219b6fb4d683951b289950505b20bb5a79dec75d09c6861", + "image_id": "sha256:db65d3cd9860cc01e2f241832f2c525299d5140073a8d683ba7ef04c0012ee85", + "image_name": "stack-node", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 3319984, + "ExitCode": 0, + "Error": "", + "StartedAt": "2026-06-21T20:00:45.903735744Z", + "FinishedAt": "0001-01-01T00:00:00Z" + }, + "entrypoint": [ + "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "cmd": null, + "args": [ + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "path": "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "env": [ + "BDAG_SNAPSHOT_URL=", + "NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NETWORK_TOPOLOGY=auto", + "BDAG_NODE_ARCHIVAL=0", + "BDAG_STORAGE_PROFILE=auto", + "P2P_PORT=8150", + "BDAG_NODE_NO_FILE_LOGGING=1", + "TMPDIR=/tmp", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_NODE_DEBUG_LEVEL=warn", + "TEMP=/tmp", + "BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral", + "BDAG_NODE_OBSOLETE_HEIGHT=20", + "BDAG_ENTRYPOINT_CHOWN_MODE=needed", + "BDAG_P2P_INTERFACE=", + "BDAG_DETECTED_NETWORK_TOPOLOGY=", + "BDAG_P2P_ADVERTISE_IP=", + "TMP=/tmp", + "BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP", + "BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc", + "BDAG_NODE_MINING_NO_PENDING_TX=1", + "BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m", + "BDAG_NETWORK=", + "SYNC_SOURCE_NODE=0", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "mounts": [ + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack/data/node", + "Destination": "/var/lib/bdagStack/node", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + }, + { + "Type": "volume", + "Name": "stack_nodeworker-data", + "Source": "/var/lib/docker/volumes/stack_nodeworker-data/_data", + "Destination": "/var/lib/bdagStack/nodeworker", + "Driver": "local", + "Mode": "rw", + "RW": true, + "Propagation": "" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker", + "Destination": "/snapshot/latest.bdsnap", + "Mode": "ro", + "RW": false, + "Propagation": "rprivate" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "Destination": "/etc/bdagStack/node.conf", + "Mode": "ro", + "RW": false, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "8297574be1926c6abc1ec64cee557495ffb4bbaf57289535c32a1e8cc8aa2d00", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:db65d3cd9860cc01e2f241832f2c525299d5140073a8d683ba7ef04c0012ee85", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "node", + "com.docker.compose.service": "node", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + } + }, + "pool": { + "present": true, + "id": "77a4d5a4129bfea999989acb9201930cdac17d65e4d741c3014ec45e38576916", + "image_id": "sha256:0f5f66595a3708eea4b084ae1a2c8a9457bb1ae28c9ddfbdf414d87faae0b03d", + "image_name": "stack-pool", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 3320963, + "ExitCode": 0, + "Error": "", + "StartedAt": "2026-06-21T20:01:26.392910027Z", + "FinishedAt": "2026-06-21T20:01:26.354216727Z" + }, + "entrypoint": [ + "/usr/local/bin/mining-pool" + ], + "cmd": null, + "args": [], + "path": "/usr/local/bin/mining-pool", + "env": [ + "POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true", + "POOL_STALE_RACE_REJECT_WINDOW_SECONDS=10", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000", + "POOL_BLOCK_TIMING_CONTROLLER_STEP_MS=250", + "POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15", + "POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1", + "POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100", + "POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS=60", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2", + "WALLET_RPC_URL=http://127.0.0.1:18545", + "POOL_BLOCK_TIMING_CONTROLLER_ENABLED=true", + "POOL_SUBMIT_RPC_URLS=", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750", + "POOL_PRIVATE_KEY=", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true", + "METRICS_ADDR=0.0.0.0:9090", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750", + "POOL_RPC_BACKENDS=", + "PPLNS_N_WORK=1000", + "POOL_INSTANCE_ID=", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_RPC_URLS=http://127.0.0.1:38131", + "POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "TMPDIR=/tmp", + "NODE_RPC_USER=test", + "NODE_RPC_URL=http://127.0.0.1:38131", + "POOL_TEMPLATE_TTL_REFRESH_MS=100", + "POOL_FEE_PERCENTAGE=1.0", + "NODE_RPC_PASS=", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20", + "POOL_BLOCK_MATURITY=10", + "POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15", + "POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5", + "TMP=/tmp", + "POOL_PAYOUT_MATURITY=4096", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "PG_URL=postgres://", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true", + "POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES=20", + "POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60", + "POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS=20", + "POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_RUNTIME_ADMIN_ENABLED=false", + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0", + "TEMP=/tmp", + "POOL_BIND_ADDR=192.168.1.100:3334", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750", + "POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT=5", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "mounts": [], + "labels": { + "com.docker.compose.config-hash": "3cc1bff3269cba209e027936c6f19ed844abc881d2e10bd0111807726ea628d1", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "pool-db:service_healthy:false", + "com.docker.compose.image": "sha256:0f5f66595a3708eea4b084ae1a2c8a9457bb1ae28c9ddfbdf414d87faae0b03d", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "pool", + "com.docker.compose.service": "pool", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + } + }, + "dashboard": { + "present": true, + "id": "ba783cd7f6fdae9229cc624418e86e28f706dbf03c9241c6c1dd4b9c046fa0a0", + "image_id": "sha256:4b04283d589857d4d9e45bf6656dd7293671879ec472a89e86d32af9647c7686", + "image_name": "stack-dashboard", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 3319231, + "ExitCode": 0, + "Error": "", + "StartedAt": "2026-06-21T20:00:15.604765751Z", + "FinishedAt": "2026-06-21T19:52:26.43438084Z", + "Health": { + "Status": "healthy", + "FailingStreak": 0, + "Log": [ + { + "Start": "2026-06-21T23:32:21.563933919+02:00", + "End": "2026-06-21T23:32:21.633716985+02:00", + "ExitCode": 0, + "Output": "" + }, + { + "Start": "2026-06-21T23:32:31.634757665+02:00", + "End": "2026-06-21T23:32:31.775368082+02:00", + "ExitCode": 0, + "Output": "" + }, + { + "Start": "2026-06-21T23:32:41.776495804+02:00", + "End": "2026-06-21T23:32:41.941282031+02:00", + "ExitCode": 0, + "Output": "" + }, + { + "Start": "2026-06-21T23:32:51.942097752+02:00", + "End": "2026-06-21T23:32:52.01079157+02:00", + "ExitCode": 0, + "Output": "" + }, + { + "Start": "2026-06-21T23:33:02.011596854+02:00", + "End": "2026-06-21T23:33:02.069193528+02:00", + "ExitCode": 0, + "Output": "" + } + ] + } + }, + "entrypoint": [ + "/usr/local/bin/dashboard" + ], + "cmd": null, + "args": [], + "path": "/usr/local/bin/dashboard", + "env": [ + "BDAG_EVM_SYNC_BACKOFF_SECONDS=60", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_TREND_BACKFILL_DAYS=30", + "TMP=/tmp", + "BDAG_CHAIN_POLL_SECONDS=2", + "BDAG_REDIS_APPENDFSYNC=everysec", + "BDAG_POOL_URL=stratum+tcp://192.168.1.100:3334", + "BDAG_TREND_BACKFILL_SLEEP_MS=100", + "BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS=4096", + "BDAG_TREND_SPARSE_SAMPLES_PER_PASS=24", + "BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS=10", + "BDAG_GLOBAL_RPC_CHUNK_SIZE=64", + "BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS=3", + "BDAG_REDIS_MAXMEMORY=256mb", + "BDAG_EVM_WS_URL=ws://host.docker.internal:18546", + "BDAG_REDIS_SOCKET=/run/dashboard-redis/redis.sock", + "BDAG_EVM_HTTP_URL=http://host.docker.internal:18545", + "BDAG_GLOBAL_MAX_INCREMENTAL_GAP=120", + "BDAG_TREND_CATCHUP_INTERVAL_SECONDS=30", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_TREND_BACKFILL_CHUNK_SIZE=128", + "BDAG_DASHBOARD_REDIS_MANAGED=1", + "BDAG_REDIS_MAXMEMORY_POLICY=noeviction", + "BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS=2048", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "ADDR=0.0.0.0:8088", + "TEMP=/tmp", + "BDAG_POOL_METRICS_URL=http://host.docker.internal:9090/metrics", + "BDAG_CHAIN_WS_READ_LIMIT_BYTES=1048576", + "BDAG_DASHBOARD_REDIS_ENABLED=1", + "BDAG_MINER_CONTROL_ENABLED=0", + "BDAG_REDIS_CONFIG=/etc/redis/redis.conf", + "BDAG_TREND_HISTORY_ENABLED=1", + "BDAG_TREND_RECENT_WARMUP_BLOCKS=3600", + "BDAG_REDIS_APPENDONLY=yes", + "BDAG_GLOBAL_BLOCK_WINDOW=600", + "BDAG_GLOBAL_FAST_WARMUP_BLOCKS=120", + "NODE_RPC_PASS=", + "BDAG_GLOBAL_SETTLE_DELAY_SECONDS=5", + "BDAG_DASHBOARD_HEALTH_STALE_SECONDS=20", + "BDAG_GLOBAL_RPC_WORKERS=4", + "BDAG_TREND_RPC_WORKERS=4", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp", + "BDAG_NODE_RPC_URL=http://host.docker.internal:38131", + "NODE_RPC_USER=test", + "TMPDIR=/tmp", + "BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "DASHBOARD_TIMEZONE=Africa/Johannesburg", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "mounts": [ + { + "Type": "volume", + "Name": "stack_dashboard-redis", + "Source": "/var/lib/docker/volumes/stack_dashboard-redis/_data", + "Destination": "/var/lib/dashboard-redis", + "Driver": "local", + "Mode": "rw", + "RW": true, + "Propagation": "" + }, + { + "Type": "bind", + "Source": "/proc/net/arp", + "Destination": "/host/proc/net/arp", + "Mode": "ro", + "RW": false, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "e03f4a8bb3888d03330d9472539c32e0a4893687873b226ce12c92c9412a2162", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "pool:service_started:false,node:service_started:false", + "com.docker.compose.image": "sha256:4b04283d589857d4d9e45bf6656dd7293671879ec472a89e86d32af9647c7686", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "dashboard", + "com.docker.compose.service": "dashboard", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + } + }, + "status-sampler": { + "present": true, + "id": "5b35a79cc6af2a3cb2ba006da91ff3fcc59fb69e8c94eee5eb1b6e03c802f1b2", + "image_id": "sha256:678391c079a5544621ac100e7a3da55a12b90c25d586ba7d4c69b66817b765ff", + "image_name": "stack-status-sampler", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 3318834, + "ExitCode": 0, + "Error": "", + "StartedAt": "2026-06-21T20:00:09.012829508Z", + "FinishedAt": "2026-06-21T19:52:33.203389363Z" + }, + "entrypoint": [ + "/bin/sh", + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "cmd": null, + "args": [ + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "path": "/bin/sh", + "env": [ + "COMPOSE_PROJECT_NAME=stack", + "NODE_RPC_USER=test", + "TMPDIR=/tmp", + "BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS=30", + "BDAG_MINING_IMPERATIVE_GUARD_UNITS=", + "BDAG_CHAIN_STATE_SELF_HEAL_COMMAND=ops/chain-state-self-heal.sh --from-systemd", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "NODE_RPC_PASS=", + "BDAG_POOL_CONTAINER=pool", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_CHAIN_STATE_SELF_HEAL_UNIT=", + "BDAG_NODE_SERVICES=node", + "BDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10", + "BDAG_STATUS_SAMPLER_FILE=/home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json", + "BDAG_NODE_RPC_URLS=node=http://host.docker.internal:38131", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_POOL_DB_CONTAINER=postgres", + "BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL=http://dashboard:8088/api/status", + "BDAG_POOL_CONTAINERS=pool", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "mounts": [ + { + "Type": "bind", + "Source": "/var/run/docker.sock", + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "9d60a4b5400ef9e3431e282622311aaaff140c76375cfffe3ac6613c28f1e091", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:678391c079a5544621ac100e7a3da55a12b90c25d586ba7d4c69b66817b765ff", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "status-sampler", + "com.docker.compose.service": "status-sampler", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Status Sampler" + } + }, + "postgres": { + "present": true, + "id": "36d4c876c62c00c471d3ad33ac51fc6e23ee14a9e379bb26b4460ab4f08997d9", + "image_id": "sha256:b395c9b05a57a43c079691104bc8468ea336a42e6148f44095e66d0c056cdd1c", + "image_name": "postgres:15-bookworm", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 3318902, + "ExitCode": 0, + "Error": "", + "StartedAt": "2026-06-21T20:00:09.10276953Z", + "FinishedAt": "2026-06-21T19:52:26.707663541Z", + "Health": { + "Status": "healthy", + "FailingStreak": 0, + "Log": [ + { + "Start": "2026-06-21T23:32:46.128581064+02:00", + "End": "2026-06-21T23:32:46.210677672+02:00", + "ExitCode": 0, + "Output": "/var/run/postgresql:5432 - accepting connections\n" + }, + { + "Start": "2026-06-21T23:32:51.211680418+02:00", + "End": "2026-06-21T23:32:51.290856148+02:00", + "ExitCode": 0, + "Output": "/var/run/postgresql:5432 - accepting connections\n" + }, + { + "Start": "2026-06-21T23:32:56.291940471+02:00", + "End": "2026-06-21T23:32:56.350035664+02:00", + "ExitCode": 0, + "Output": "/var/run/postgresql:5432 - accepting connections\n" + }, + { + "Start": "2026-06-21T23:33:01.351096793+02:00", + "End": "2026-06-21T23:33:01.442231522+02:00", + "ExitCode": 0, + "Output": "/var/run/postgresql:5432 - accepting connections\n" + }, + { + "Start": "2026-06-21T23:33:06.443555067+02:00", + "End": "2026-06-21T23:33:06.523744661+02:00", + "ExitCode": 0, + "Output": "/var/run/postgresql:5432 - accepting connections\n" + } + ] + } + }, + "entrypoint": [ + "docker-entrypoint.sh" + ], + "cmd": [ + "postgres" + ], + "args": [ + "postgres" + ], + "path": "docker-entrypoint.sh", + "env": [ + "POSTGRES_DB=bdagpool", + "POSTGRES_PASSWORD=", + "TMPDIR=/tmp", + "TMP=/tmp", + "TEMP=/tmp", + "POSTGRES_USER=bdag_pool", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/15/bin", + "GOSU_VERSION=1.19", + "LANG=en_US.utf8", + "PG_MAJOR=15", + "PG_VERSION=15.18-1.pgdg12+1", + "PGDATA=/var/lib/postgresql/data" + ], + "mounts": [ + { + "Type": "volume", + "Name": "stack_postgres-data", + "Source": "/var/lib/docker/volumes/stack_postgres-data/_data", + "Destination": "/var/lib/postgresql/data", + "Driver": "local", + "Mode": "rw", + "RW": true, + "Propagation": "" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis/sql/pool-schema.sql", + "Destination": "/docker-entrypoint-initdb.d/01-pool-schema.sql", + "Mode": "ro", + "RW": false, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "65ffec4edf19c98fb1bc7919809f6a4a013270b8c2e5878f5528ed49e4002ab1", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:b395c9b05a57a43c079691104bc8468ea336a42e6148f44095e66d0c056cdd1c", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "postgres", + "com.docker.compose.service": "pool-db", + "com.docker.compose.version": "2.40.3" + } + }, + "watchdog": { + "present": true, + "id": "cbc6353dfe48204167b3cfebe35cf9be20a0434c06b2199cb2eb34d2643a72b4", + "image_id": "sha256:355e333d19d056bd6e5ed8f9b7231747bd0ad72ca26743a796065f842ccddf51", + "image_name": "stack-watchdog", + "state": { + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 137, + "Error": "", + "StartedAt": "2026-06-21T19:51:15.127534786Z", + "FinishedAt": "2026-06-21T19:52:33.210436649Z" + }, + "entrypoint": [ + "/bin/sh", + "-c", + "python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop" + ], + "cmd": null, + "args": [ + "-c", + "python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop" + ], + "path": "/bin/sh", + "env": [ + "NODE_RPC_PASS=", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "TMPDIR=/tmp", + "BDAG_POOL_CONTAINER=pool", + "BDAG_STATUS_SOURCE_URL=http://dashboard:8088/api/status", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "COMPOSE_PROJECT_NAME=stack", + "BDAG_POOL_DB_CONTAINER=postgres", + "BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS=300", + "BDAG_POOL_HOST=192.168.1.100", + "BDAG_POOL_CONTAINERS=pool", + "BDAG_NODE_SERVICES=node", + "NODE_RPC_USER=test", + "BDAG_WATCHDOG_INTERVAL=60", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "mounts": [ + { + "Type": "bind", + "Source": "/var/run/docker.sock", + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "8284d8a6f838e54e658dd33722504cc81f08c3cb35e1c85609946da1316b0894", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:355e333d19d056bd6e5ed8f9b7231747bd0ad72ca26743a796065f842ccddf51", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "watchdog", + "com.docker.compose.service": "watchdog", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Watchdog" + } + }, + "sentinel": { + "present": true, + "id": "4cb2ab330489cc3a2715eb24f6879c2a82c0dc218dfb08b97025d29c80c142aa", + "image_id": "sha256:1c1e14c011edba83f7a45a24382cebefca8017e9fd23f1109bfd6cc6f774aa80", + "image_name": "stack-sentinel", + "state": { + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 137, + "Error": "", + "StartedAt": "2026-06-21T19:51:15.130469576Z", + "FinishedAt": "2026-06-21T19:52:33.199143907Z" + }, + "entrypoint": [ + "/bin/sh", + "-c", + "python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep ${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done" + ], + "cmd": null, + "args": [ + "-c", + "python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep ${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done" + ], + "path": "/bin/sh", + "env": [ + "BDAG_POOL_CONTAINERS=pool", + "BDAG_NODE_SERVICES=node", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "BDAG_POOL_CONTAINER=pool", + "BDAG_SENTINEL_STATUS_URL=http://dashboard:8088/api/status", + "NODE_RPC_USER=test", + "TMPDIR=/tmp", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "COMPOSE_PROJECT_NAME=stack", + "BDAG_SENTINEL_INTERVAL_SECONDS=60", + "NODE_RPC_PASS=", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "BDAG_SENTINEL_USER_SERVICES=", + "BDAG_SENTINEL_USER_TIMERS=", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_POOL_DB_CONTAINER=postgres", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "mounts": [ + { + "Type": "bind", + "Source": "/var/run/docker.sock", + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + }, + { + "Type": "bind", + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "RW": true, + "Propagation": "rprivate" + } + ], + "labels": { + "com.docker.compose.config-hash": "ff1d8885724e3cb12bf66b0ab5c13aa6a64436d39b4f08ee449ead92ac0b95e8", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:1c1e14c011edba83f7a45a24382cebefca8017e9fd23f1109bfd6cc6f774aa80", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "sentinel", + "com.docker.compose.service": "sentinel", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Sentinel" + } + } + }, + "node_runtime": { + "node_conf_path": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "node_conf": "# BlockDAG node configuration for the Docker Compose stack.\n\n# P2P listen\nlisten=0.0.0.0\nport=8150\n\n# BDAG RPC listen\nrpclisten=0.0.0.0:38131\n\n# EVM RPC listen\nevm.http.addr=0.0.0.0\nevm.http.port=18545\nevm.ws.addr=0.0.0.0\nevm.ws.port=18546\n\n\n# Bootstrap peers (mainnet seed nodes). Add one addpeer=... line per complete\n# BDAG P2P multiaddr. Keep only public peers that are currently reachable.\n\n\n\n\naddpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo\naddpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\naddpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz\naddpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD\naddpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU\naddpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm\naddpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv\naddpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn\naddpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta\naddpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ\naddpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME\naddpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus\naddpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG\naddpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ\naddpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY\naddpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G\naddpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7\naddpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1\naddpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP\naddpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X\naddpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB\naddpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9\n\n# Peer limit\nmaxpeers=350\ncache=6144\ncache.database=70\n\n# RPC auth. Must match NODE_RPC_USER / NODE_RPC_PASS in .env.\nrpcuser=test\nrpcpass=test\n\n# Storage paths\ndatadir=/var/lib/bdagStack/node\nlogdir=/var/log/bdagStack\n\n# Native metrics endpoint\nmetrics=true\nprofile=0.0.0.0:6060\n\n# Keep empty unless node mining is intentionally enabled.\nminingaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\nmodules=Blockdag,miner\n\nevm.http.api=eth,net,web3,txpool,debug\nhttp.readtimeout=45s\nhttp.writetimeout=45s\nhttp.idletimeout=45s\nevmenv=\"--rpc.allow-unprotected-txs --metrics --metrics.addr 0.0.0.0 --metrics.port 6060 --cache 6144\"\nrpcmaxclients=64\nrpcmaxclientsperhost=16\nrpcmaxconcurrentreqs=500\nrpcrequesttimeout=60s\nhttp.ratelimit=1800\nhttp.rateburst=3200\n", + "env_path": "/home/jeremy/blockdag-source/stack-redis/.env", + "env_redacted": "COMPOSE_PROJECT_NAME=stack\nDOCKERFILE=dockerfile-dev\nDOCKER_PLATFORM=linux/amd64\n# Optional overrides for dockerfile-dev named build contexts.\"\n# BLOCKDAG_CORECHAIN_CONTEXT=../blockdag-corechain\n# POOL_SRC_CONTEXT=../pool\n# CPU_MINER_SRC_CONTEXT=../cpu-miner\nCOLLECTOR_SRC_CONTEXT=/home/jeremy/blockdag-source/collector\n# DASHBOARD_SRC_CONTEXT=../dashboard2\"\n# Paths are relative to tarball root (same directory as docker-compose.yml).\n# To import a snapshot during build, place latest.bdsnap in the payload root and\n# set SNAPSHOT_PATH=./latest.bdsnap. The default marker means \"build without a\n# baked-in snapshot.\"\nSNAPSHOT_PATH=docker/no-snapshot.marker\n# SNAPSHOT_PATH=docker/latest.bdsnap\"\n\n# Snapshot download link for this node. The installer writes this based on the\n# selected install mode. Naming convention on the snapshot host:\n# latest.bdsnap -> non-archive (pruned) snapshot\n# latest-archive.bdsnap -> archive (full history) snapshot\n# The node entrypoint downloads from this URL at first start when no local\n# snapshot or chain data exists.\nBDAG_SNAPSHOT_URL=\n# Set to 1 to run the node in archival mode (--archival; consensus keeps all\n# blocks instead of pruning). Archive nodes should bootstrap from the archive\n# snapshot so historical data is actually present.\nBDAG_NODE_ARCHIVAL=0\n# Optional compose services:\n# blank -> base stack only\n# miner -> include CPU miner service (requires DOCKERFILE=dockerfile-dev)\n# COMPOSE_PROFILES=miner\n\n# Dashboard repository. Release builds always use the main branch.\nDASHBOARD_REPO=https://github.com/BlockdagEngineering/redis-dash.git\nP2P_PORT=8150\nBDAG_RPC_PORT=38131\nEVM_HTTP_PORT=18545\nEVM_WS_PORT=18546\nWALLET_RPC_URL=http://127.0.0.1:18545\nNODE_METRICS_PORT=6060\nPOOL_BIND_PORT=3334\nPOOL_API_PORT=9090\nCOLLECTOR_HOST_PORT=9280\nDASHBOARD_HOST_PORT=8088\n# Dashboard UI display timezone.\nDASHBOARD_TIMEZONE=Africa/Johannesburg\n# NODE_DATA_DIR=./node-data\n\n# Required for any pool/miner deployment. Do not use the zero address.\n# POOL_COINBASE_ADDRESS may be set separately when it must differ from the\n# displayed miner/pool identity; otherwise set MINING_POOL_ADDRESS only.\nMINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\n# Storage placement for constrained mining appliances. Release installers keep\n# growing node chain data on capacity storage, then move frequent small writes\n# such as Postgres and dashboard/runtime state to internal storage when it has\n# enough free space.\nBDAG_STORAGE_PROFILE=auto\nBDAG_CHAIN_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_NODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nBDAG_POSTGRES_DATA_DIR=./data/postgres\nBDAG_RUNTIME_DIR=./ops/runtime\nBDAG_STORAGE_MIN_CHAIN_FREE_GIB=50\nBDAG_STORAGE_MIN_RUNTIME_FREE_GIB=4\n\n# Ephemeral scratch policy. Small temporary files belong on RAM-backed tmpfs so\n# they do not add avoidable writes to the USB chain device. Large snapshot and\n# chain staging must stay on capacity storage unless explicitly sized.\nBDAG_EPHEMERAL_TMPFS_ENABLED=1\nBDAG_EPHEMERAL_DIR=/run/bdag-pool\nBDAG_HOST_TMPDIR=/run/bdag-pool/tmp\nBDAG_CONTAINER_TMPFS_SIZE=128m\nBDAG_NODE_TMPFS_SIZE=512m\nBDAG_NODE_SHM_SIZE=512m\nBDAG_BUILD_TMPDIR=./.build-tmp\nBDAG_BUILD_CACHE_WARN_GIB=4\nBDAG_ROOT_FREE_WARN_GIB=6\nBDAG_ROOT_FREE_FAIL_GIB=2\n\n# Single P2P host port used by Docker publishing, firewall, and local peer\n# advertisement.\nBDAG_P2P_PROTOCOLS=tcp\n# Standard mining-host performance defaults. The release topology has one\n# production node with a direct pool-to-node RPC path.\nBDAG_NODE_CACHE_MB=6144\nBDAG_EVM_CACHE_MB=6144\nBDAG_NODE_CACHE_DATABASE_PERCENT=70\nBDAG_NODE_OBSOLETE_HEIGHT=20\nBDAG_NODE_MINING_NO_PENDING_TX=1\nBDAG_BLOCK_READ_AHEAD_KB=1024\nBDAG_BLOCK_NR_REQUESTS=256\nBDAG_NODE_CPU_SHARES=6144\nBDAG_POOL_CPU_SHARES=5120\nBDAG_POOL_DB_CPU_SHARES=4096\nBDAG_DASHBOARD_CPU_SHARES=128\nBDAG_NODE_MEMORY_LOW=768M\nBDAG_POOL_MEMORY_LOW=256M\nBDAG_POOL_DB_MEMORY_LOW=512M\nBDAG_DASHBOARD_MEMORY_LOW=64M\nBDAG_MINING_ACTIVE_NODE_NICE=-10\nBDAG_MINING_POOL_NICE=-8\nBDAG_OBSERVABILITY_NICE=15\nBDAG_TUNE_NET_QDISC=1\nBDAG_STACK_NET_IFACE=\nPOOL_RPC_BACKENDS=\n\n# Release install support services. Fresh appliance installs apply the\n# non-destructive host profile by default, then install P2P/IPFS support timers\n# and the mining-host tuning timer. Set the strict toggles to fail the install\n# instead of warning when host-level service installation cannot complete.\nBDAG_INSTALL_APPLIANCE_HOST_PROFILE=1\nBDAG_INSTALL_APPLIANCE_PROFILE_DISABLE_SERVICES=0\nBDAG_INSTALL_APPLIANCE_PROFILE_RELOAD_DOCKER=1\nBDAG_INSTALL_APPLIANCE_PROFILE_STRICT=0\nBDAG_INSTALL_STACK_SUPPORT_SERVICES=1\nBDAG_INSTALL_STACK_SUPPORT_SERVICES_STRICT=0\n\n# Enables localhost/private-network pool runtime admin endpoints. Keep the pool\n# metrics/admin port bound to localhost on public hosts.\nPOOL_RUNTIME_ADMIN_ENABLED=false\nMETRICS_ADDR=0.0.0.0:9090\nPOOL_GBT_MIN_INTERVAL_MS=1100\nPOOL_GBT_PRESSURE_INTERVAL_MS=500\nPOOL_GBT_PRESSURE_WINDOW_SECONDS=10\nPOOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15\nPOOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30\n# Host network discovery is only for choosing this node's advertised P2P address.\n# Peer candidates should be complete P2P multiaddrs.\nBDAG_NETWORK_TOPOLOGY=auto\nBDAG_DETECTED_NETWORK_TOPOLOGY=\nBDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\nBDAG_ASIC_LAN_CIDRS=192.168.1.0/24\n# Optional route to miners on a remote LAN reached through a routed gateway\n# (e.g. a ZeroTier peer). When set, the miner-route compose service keeps the\n# host route alive so the watchdog can reach the miner HTTP API. Leave empty\n# when miners are on the host LAN.\nBDAG_MINER_ROUTE_SUBNET=\nBDAG_MINER_ROUTE_GATEWAY=\nBDAG_MINER_ROUTE_DEV=\n# Docker bridge/container networks must not be treated as ASIC LANs. Override\"\n# only if a real ASIC LAN intentionally uses these ranges.\nBDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12\nBDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0\nBDAG_P2P_ADVERTISE_IP=\nBDAG_P2P_INTERFACE=\nBDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1\nBDAG_CHAIN_PEERSTORE_LOG_TAIL=8000\nEXTRA_PEER_ADDRESSES=\n# When enabled by installer/watchdog for attached ASICs, this must include\n# --miner, --miningaddr=, and --maxinbound=1 on constrained\n# USB/router hosts. Do not add --allowminingwhennearlysynced or\n# --allowsubmitwhennotsynced; template/readiness health must fail closed when\n# P2P freshness or sync safety is unavailable.\nBDAG_NODE_MINING_ARGS=\nBDAG_NODE_CHAIN_RPC_TIMEOUT=8.0\nBDAG_NODE_CHAIN_RPC_RETRIES=2\nBDAG_POOL_RPC_REFUSED_WARN_SECONDS=120\nBDAG_HOST_PRESSURE_IOWAIT_WARN_PERCENT=25\nBDAG_HOST_PRESSURE_IOWAIT_WARN_SAMPLES=3\nBDAG_HOST_PRESSURE_HISTORY_SAMPLES=6\nBDAG_SHARED_STATUS_CACHE_ENABLED=1\nBDAG_SHARED_STATUS_CACHE_SECONDS=3.0\nBDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS=120\nBDAG_HOST_PROFILE=auto\nBDAG_ADAPTIVE_CONCURRENCY_ENABLED=1\nBDAG_ADAPTIVE_IOWAIT_WARN_PERCENT=25\nBDAG_ADAPTIVE_IO_SOME_AVG10_WARN=20.0\nBDAG_ADAPTIVE_CPU_SOME_AVG10_WARN=80.0\nBDAG_ADAPTIVE_CHAIN_RPC_WARN_MS=1000\nBDAG_STATUS_SAMPLER_ENABLED=1\nBDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10\nBDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120\nBDAG_COLLECTOR_DIRECT_STATUS_FALLBACK=0\nBDAG_COLLECTOR_STATUS_CACHE_SECONDS=10\nBDAG_COLLECTOR_SAMPLER_CACHE_SECONDS=120\nBDAG_COLLECTOR_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_CHAIN_STATE_SELF_HEAL_ENABLED=1\nBDAG_CHAIN_STATE_SELF_HEAL_UNIT=bdag-chain-state-self-heal.service\nBDAG_MINING_IMPERATIVE_CHAIN_STATE_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_SECONDS=900\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS=1000\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_GAP_GROWTH_BLOCKS=60\nBDAG_CHAIN_STATE_REUSE_EXISTING_SNAPSHOT=1\n# Optional trusted restore source for unattended chain-state self-heal.\"\n# Use key-based SSH for remote rsync sources; do not store passwords here.\nBDAG_CHAIN_STATE_RESTORE_SOURCE=\nBDAG_CHAIN_STATE_RESTORE_SNAPSHOT=\nBDAG_CHAIN_STATE_RESTORE_SSH_COMMAND=\nBDAG_DASHBOARD_DIRECT_STATUS_FALLBACK=0\nBDAG_DASHBOARD_STATUS_CACHE_SECONDS=10\nBDAG_DASHBOARD_SAMPLER_CACHE_SECONDS=120\nBDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_EARNINGS_SAMPLER_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_GLOBAL_SAMPLER_INTERVAL_SECONDS=60\nBDAG_OPS_UID=1000\nBDAG_OPS_GID=1000\nBDAG_DOCKER_SOCKET_GID=115\nBDAG_GLOBAL_CACHE_TTL_SECONDS=60\nBDAG_GLOBAL_RPC_WORKERS=4\nBDAG_GLOBAL_BLOCK_WINDOW=600\nBDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOTS=1\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_HOURS=720\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WINDOW_BLOCKS=64\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WORKERS=12\nBDAG_DASHBOARD_HISTORY_REBUILD_PRESERVE_ASIC_HISTORY=1\n# Optional native mining RPC list for Global chain-production stats.\"\n# EVM RPC variables are wallet/balance sources only and must not drive Global mining attribution.\nBDAG_GLOBAL_CHAIN_RPC_URLS=\nBDAG_MINER_SCAN_WORKERS=64\nBDAG_MINER_HASHRATE_PROBE_WORKERS=8\nBDAG_BACKGROUND_MAINTENANCE_BACKOFF_ENABLED=1\nBDAG_BACKGROUND_MAINTENANCE_SYNC_BACKOFF_BLOCKS=0\nBDAG_BACKGROUND_MAINTENANCE_IOWAIT_WARN_PERCENT=25\nBDAG_BACKGROUND_MAINTENANCE_IO_SOME_AVG10_WARN=20.0\nBDAG_BACKGROUND_MAINTENANCE_CPU_SOME_AVG10_WARN=80.0\nBDAG_BACKGROUND_MAINTENANCE_CHAIN_RPC_WARN_MS=1000\nBDAG_BACKGROUND_MAINTENANCE_POOL_READY_TASKS=rawdatadir_publish,ipfs_content_sidecar,ipfs_segment_writer\nBDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=2\nBDAG_ENTRYPOINT_CHOWN_MODE=needed\nBDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\n# Current Compose/service-name topology. Source Compose intentionally does not\n# pin container_name; Docker Compose may append project and ordinal suffixes to\n# concrete container names.\nBDAG_POOL_CONTAINER=pool\nBDAG_POOL_CONTAINERS=pool\nBDAG_POOL_DB_CONTAINER=postgres\nBDAG_NODE_SERVICES=node\nBDAG_STACK_SERVICES=postgres,node,pool\nBDAG_NODE_RPC_URLS=node=http://127.0.0.1:38131\n\n# Optional pre-start P2P snapshot bootstrap. When the node datadir is empty,\n# the node image uses ordinary trusted/addpeer candidates for FastSnap V2 first,\n# imports the verified snapshot, then normal FastSync catches the remaining tail.\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=90s\nBDAG_FASTSNAP_ARTIFACT_V2=1\n# Prefer Fast Artifact Sync V2 directory artifacts when a peer offers them.\n# This avoids building/importing a temporary .bdsnap archive. The archive\n# snapshot RPC path is used only when the serving peer lacks directory support.\nBDAG_FASTSNAP_DIRECTORY_MODE=1\nBDAG_FASTSNAP_DIRECTORY_STAGING=\nBDAG_FASTSNAP_DIRECTORY_REPLACE_EXISTING=1\nBDAG_FASTSNAP_DIRECTORY_MOVE_STAGING=1\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_PARALLELISM=4\nBDAG_FASTSNAP_LEDGER=\n# Archive FastSnap seed serving is deprecated for release installs.\n# The raw datadir FastArtifact source sidecar below is the preferred sync-source\n# path. Keep this disabled unless an offline export workflow is being\n# investigated away from the live mining host.\nBDAG_FASTSNAP_SEED_TIMER_ENABLED=0\n# Raw datadir FastArtifact source sidecar. Installer auto-enables the timer only\n# when chain/stage/artifact paths are not USB/removable and the host has enough\n# CPU, RAM, and disk headroom. Publishes require an explicit operator-approved\n# finalization window.\nSYNC_SOURCE_NODE=0\nBDAG_RAWDATADIR_ARTIFACT_BASE=./data-restore/rawdatadir\nBDAG_RAWDATADIR_ARTIFACT_KEEP=3\nBDAG_RAWDATADIR_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_REQUIRE_EVM_REFERENCE_FRESH=1\nBDAG_RAWDATADIR_MAX_EVM_REFERENCE_LAG=1000\nBDAG_PUBLIC_RPC_URLS=bdagscan-rpc=https://rpc.bdagscan.com,blockdag-engineering-rpc=https://rpc.blockdag.engineering\nBDAG_RAWDATADIR_SIDECAR_SOURCE=\nBDAG_RAWDATADIR_SIDECAR_DIR=\nBDAG_RAWDATADIR_SIDECAR_CONTENT_MODE=auto\nBDAG_RAWDATADIR_SIDECAR_CONTENT_BASE=./data-restore/rawdatadir-sidecar-content\nBDAG_RAWDATADIR_SIDECAR_CONTENT_KEEP=2\nBDAG_RAWDATADIR_SIDECAR_CONTENT_CHUNK_SIZE=67108864\nBDAG_RAWDATADIR_SIDECAR_CONTENT_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_SIDECAR_CONTENT_ALLOW_HOT_PUBLISH=0\nBDAG_RAWDATADIR_OPEN_SIDECAR_ENABLED=1\nBDAG_RAWDATADIR_OPEN_SIDECAR_BASE=./data-restore/rawdatadir-sidecar-open/mainnet\nBDAG_RAWDATADIR_OPEN_SIDECAR_KEEP=12\nBDAG_RAWDATADIR_SIDECAR_RSYNC_BWLIMIT=4096\nBDAG_RAWDATADIR_SIDECAR_DELAY_UPDATES=0\nBDAG_RAWDATADIR_ACTIVE_SERVICE=node\nBDAG_RAWDATADIR_FINALIZE=0\nBDAG_RAWDATADIR_MIN_FREE_GIB=100\nBDAG_RAWDATADIR_FREE_SPACE_MULTIPLIER=2.5\nBDAG_RAWDATADIR_REQUIRE_STATE_ROOT=1\nBDAG_RAWDATADIR_SIDECAR_USE_SUDO=auto\nBDAG_RAWDATADIR_ARCHIVE_USE_SUDO=auto\nBDAG_RAWDATADIR_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_RAWDATADIR_TRUSTED_SIGNERS=\n# Optional Fast Artifact Sync V2 directory hot-stage serving. Set these only on\n# nodes that maintain a verified directory artifact mirror. The manifest path is\n# allowed to be a sidecar outside the directory to avoid copying control files\n# into a future install target.\nBDAG_FASTSYNC_ARTIFACT_DIRECTORY=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST=\n\n# Lazy IPFS content sidecar. The rawdatadir sidecar seals immutable signed\n# file/chunk generations into BDAG_RAWDATADIR_SIDECAR_CONTENT_BASE; IPFS pins\n# only publishable generations after mining and host resource gates pass. IPFS\n# is byte transport; signed manifests and consensus validation remain\n# authoritative.\nBDAG_IPFS_CONTENT_SIDECAR_MODE=auto\nBDAG_IPFS_CONTENT_ARTIFACT_DIR=./data-restore/rawdatadir-sidecar-content/current\nBDAG_IPFS_CONTENT_ARTIFACT_MANIFEST=./data-restore/rawdatadir-sidecar-content/current/manifest.json\nBDAG_IPFS_CONTENT_ALLOW_UNSIGNED_ARTIFACT=0\nBDAG_IPFS_CONTENT_PUBLISH_IPNS=0\nBDAG_IPFS_CONTENT_IPNS_KEY=\nBDAG_IPFS_CONTENT_REPUBLISH_IPNS_WHILE_WAITING=1\nBDAG_IPFS_CONTENT_IPNS_TTL=1m\nBDAG_IPFS_CONTENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_CONTENT_ADD_ARGS=\"--recursive --cid-version=1 --raw-leaves --pin=true --quieter\"\nBDAG_IPFS_CONTENT_DISCOVERY_FILE=./ops/ipfs-content-discovery.json\nBDAG_IPFS_CONTENT_LATEST_IPNS=/ipns/k51qzi5uqu5djjlh4vxtmzyswx0qk4s3wdlf3yrpkszp38gq5sl71zcgmmc3jk\nBDAG_IPFS_CONTENT_DEFAULT_INDEX_CID=bafkreia7jk2ljqi3raiohugp6nw3633njfp7jmnuvqh47po52et4kupu2a\nBDAG_IPFS_CONTENT_DEFAULT_ROOT_CID=\nBDAG_IPFS_CONTENT_STATUS_FILE=./ops/runtime/ipfs-content-sidecar-status.json\nBDAG_IPFS_CONTENT_LATEST_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\nBDAG_IPFS_SEGMENT_WRITER_MODE=auto\nBDAG_IPFS_SEGMENT_START_POLICY=live_tail\nBDAG_IPFS_SEGMENT_FINALITY_LAG_ORDERS=600\nBDAG_IPFS_SEGMENT_ORDERS_PER_SEGMENT=300\nBDAG_IPFS_SEGMENT_MAX_SEGMENTS_PER_RUN=1\nBDAG_IPFS_SEGMENT_MAX_RPC_PER_SECOND=25\nBDAG_IPFS_SEGMENT_RPC_TIMEOUT=8\nBDAG_IPFS_SEGMENT_BLOCK_RPC_RETRIES=2\nBDAG_IPFS_SEGMENT_PUBLISH_IPNS=0\nBDAG_IPFS_SEGMENT_IPNS_KEY=\nBDAG_IPFS_SEGMENT_IPNS_TTL=1m\nBDAG_IPFS_SEGMENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_SEGMENT_STATUS_FILE=./ops/runtime/ipfs-content/segment-writer-status.json\nBDAG_IPFS_SEGMENT_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\n\n# FastSync candidates for new/empty nodes and startup peers. Entries must be\n# complete P2P multiaddrs with peer IDs. Address class is not a sync mode or\n# priority signal; libp2p/FastArtifact probing should pick useful peers by\n# measured P2P latency, manifest/chunk response, and sustained transfer.\nBDAG_FASTSYNC_PEER_ORDERING=p2p-latency\nBDAG_FASTSYNC_APPEND_ADDPEERS=1\nBDAG_FASTARTIFACTSYNC_ENABLED=1\nBOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_RANGE_BLOCKS=1024\nBDAG_FASTSYNC_PREPROCESS_WORKERS=1\nBDAG_SYNC_COORDINATOR_ACCELERATE_FASTSYNC=1\nBDAG_SYNC_COORDINATOR_FAST_RESTART_COOLDOWN_SECONDS=900\nBDAG_SYNC_COORDINATOR_RESTART_ON_STALE_IMPORT=1\nBDAG_CATCHUP_PAUSE_ENABLED=1\nBDAG_CATCHUP_PAUSE_ON_SYNCING=1\nBDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300\nBDAG_CATCHUP_IO_PRESSURE_PAUSE_ENABLED=1\nBDAG_CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS=25\nBDAG_CATCHUP_IOWAIT_WARN_PERCENT=15\nBDAG_CATCHUP_IO_SOME_AVG10_WARN=20.0\nBDAG_CATCHUP_IO_FULL_AVG10_WARN=10.0\nBDAG_CATCHUP_NODE_RECREATE_ENABLED=1\nBDAG_CATCHUP_NODE_CACHE_MB=6144\nBDAG_CATCHUP_NODE_CACHE_MIN_MB=1024\nBDAG_CATCHUP_NODE_CACHE_MEMORY_PERCENT=40\nBDAG_FAST_CATCHUP_ARTIFACT_MODE=auto\nBDAG_FAST_CATCHUP_ARTIFACT_RETRY_SECONDS=300\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_BEHIND_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_GAIN_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_TRUST_ON_FIRST_SIGNED=1\nBDAG_FAST_CATCHUP_ALLOW_UNSIGNED_ARTIFACTS=0\nBDAG_FAST_CATCHUP_ARTIFACT_TIMEOUT=21600s\nNODE_RPC_USER=test\nNODE_RPC_PASS=\nNODE_RPC_URLS=http://127.0.0.1:38131\n# Optional block-candidate submit override. Leave blank to use NODE_RPC_URLS.\"\n# Named entries are allowed: local=http://node:38131\nPOOL_SUBMIT_RPC_URLS=\n# Extra node flags.\nNODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\nBDAG_FASTSYNC_SNAPSHOT_PATH=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_ID=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_HEX=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST_TTL=24h\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS_FILE=/etc/bdagStack/node.conf\nBDAG_FASTSNAP_AUTO_MAX_PEERS=3\nBDAG_FASTSNAP_LEGACY_FALLBACK=0\n# Production pool deployments are mainnet-only. Install/runtime scripts reject\n# any rawdatadir or FastSnap network value other than mainnet.\nBDAG_FASTSNAP_NETWORK=mainnet\nBDAG_RAWDATADIR_NETWORK=mainnet\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=300s\nBDAG_FASTSNAP_PARALLELISM=8\nBDAG_FASTSNAP_RETRIES=5\nBDAG_FASTSNAP_RETRY_DELAY=10s\nBDAG_FASTSNAP_TRUSTED_SIGNERS=\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_KEEP_ARCHIVE=0\nPOSTGRES_USER=bdag_pool\nPOSTGRES_DB=bdagpool\nPOSTGRES_PASSWORD=\n# ---------------------------------------------------------------------------\n# Mining pool \u2014 passed into the `pool` container environment.\n# ---------------------------------------------------------------------------\nPOOL_BIND_ADDR=192.168.1.100:3334\nPPLNS_N_WORK=1000\nPOOL_FEE_PERCENTAGE=1.0\nPOOL_BLOCK_MATURITY=10\nPOOL_PAYOUT_MATURITY=4096\n# CPU miner (only used when COMPOSE_PROFILES includes \"miner\").\nMINER_POOL_URL=stratum+tcp://192.168.1.100:3334\nMINER_POOL_PASS=\nMINER_WORKERS=1\n\n\n\n# ASIC block-candidate timing pinned to the best observed 2026-06-21\n# 05:40-05:57 SAST production window.\nPOOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true\nPOOL_TEMPLATE_TTL_REFRESH_MS=100\nPOOL_SUBMIT_STALE_BLOCK_CANDIDATES=false\nPOOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750\nPOOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100\nPOOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true\nPOOL_STALE_RACE_REJECT_WINDOW_SECONDS=10\nPOOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1\nPOOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0\nPOOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60\nPOOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true\nPOOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true\nSTACK_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nBLOCKDAG_CORECHAIN_CONTEXT=/home/jeremy/blockdag-source/blockdag-corechain\nPOOL_SRC_CONTEXT=/home/jeremy/blockdag-source/pool\nCPU_MINER_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nDASHBOARD_SRC_CONTEXT=/home/jeremy/blockdag-source/redis-dash\nCOMPOSE_PROFILES=\nNODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nSNAPSHOT_HOST_PATH=./docker/no-snapshot.marker\nBDAG_REQUIRE_SNAPSHOT=0\nBDAG_ENABLE_NODE_MINING=1\nBDAG_NODE_MODULES=Blockdag,miner\nBDAG_POOL_HOST=192.168.1.100\nBDAG_POOL_URL=stratum+tcp://192.168.1.100:3334\nBDAG_MINER_SCAN_TARGET=192.168.1.0/24\nBDAG_MINER_CONTROL_ENABLED=0\nDASHBOARD_BIND=127.0.0.1\nNODE_RPC_URL=http://127.0.0.1:38131\nPOOL_DB_HOST=127.0.0.1\nPOOL_DB_PORT=55432\nMINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_PRIVATE_KEY=\nPOOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\nPOOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\nPOOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp\nPOOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30\nPOOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com\nBDAG_POOL_START_GATE_MIN_PEERS=2\nBDAG_NODE_DEBUG_LEVEL=warn\nBDAG_NODE_NO_FILE_LOGGING=1\n", + "docker_exec_ps": "UID PID PPID C STIME TTY TIME CMD\nroot 1 0 0 20:00 ? 00:00:00 runuser -u bdagStack -g bdagStack -- /usr/local/bin/nodeworker --node-binary=/usr/local/bin/blockdag-node --node-args=--configfile /etc/bdagStack/node.conf --addpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz --addpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU --addpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus --addpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X --addpeer=/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops --addpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1 --addpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo --addpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7 --addpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP --addpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP --addpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD --addpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm --addpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv --addpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn --addpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta --addpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ --addpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME --addpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG --addpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ --addpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY --addpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G --addpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB --addpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9 --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0 --obsoleteheight=20 --miningnopendingtx --debuglevel=warn --nofilelogging --rpc-url=ws://127.0.0.1:18546 --dag-rpc-url=http://127.0.0.1:38131 --persist-root=/var/lib/bdagStack/nodeworker --health-min-peers=2 --rollout-window=30m --health.liveness-timeout=5m\nbdagSta+ 84 1 0 20:00 ? 00:00:07 /usr/local/bin/nodeworker --node-binary=/usr/local/bin/blockdag-node --node-args=--configfile /etc/bdagStack/node.conf --addpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz --addpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU --addpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus --addpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X --addpeer=/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops --addpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1 --addpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo --addpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7 --addpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP --addpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP --addpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD --addpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm --addpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv --addpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn --addpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta --addpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ --addpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME --addpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG --addpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ --addpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY --addpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G --addpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB --addpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9 --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0 --obsoleteheight=20 --miningnopendingtx --debuglevel=warn --nofilelogging --rpc-url=ws://127.0.0.1:18546 --dag-rpc-url=http://127.0.0.1:38131 --persist-root=/var/lib/bdagStack/nodeworker --health-min-peers=2 --rollout-window=30m --health.liveness-timeout=5m\nbdagSta+ 92 84 99 20:00 ? 02:06:32 /usr/local/bin/blockdag-node --configfile /etc/bdagStack/node.conf --addpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz --addpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU --addpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus --addpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X --addpeer=/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops --addpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1 --addpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo --addpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7 --addpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP --addpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP --addpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD --addpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm --addpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv --addpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn --addpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta --addpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ --addpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME --addpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG --addpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ --addpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY --addpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G --addpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB --addpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9 --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0 --obsoleteheight=20 --miningnopendingtx --debuglevel=warn --nofilelogging\nroot 171 0 50 21:33 ? 00:00:00 ps -ef", + "datadir_mount": "/home/jeremy/blockdag-source/stack/data/node -> /var/lib/bdagStack/node", + "required_startup_invariant": "Blockdag and miner modules only, miner enabled, non-zero mining address configured. Do not add the separate P2P node module; peer multiaddrs are expected and are not that module flag." + }, + "live_evidence": { + "dashboard_status": { + "status": "ok", + "overall": "ok", + "mode": "mining", + "can_accept_shares": true, + "can_submit_blocks": true, + "chain_block_count": "12198136", + "updated_at": "2026-06-21T21:33:09Z", + "sync_progress": { + "best_peer_mainorder": 12198135, + "chain_block_count": 12198136, + "chain_rpc_source": "getBlockCount", + "chain_syncing": false, + "current_block": 12198136, + "eth_syncing": false, + "evm_block_count": "11831670", + "evm_chain_syncing": false, + "evm_rpc_url": "http://host.docker.internal:18545", + "highest_block": 12198136, + "native_is_current": true, + "p2p_connections": 11, + "p2p_network_gap": 0, + "p2p_network_height": 12198135, + "peer_count": 11, + "peer_mainorder_gap": 0, + "remaining_blocks": 0, + "source": "native-rpc", + "status": "synced" + }, + "pool_metrics": { + "accepted_block_recent": true, + "accepted_block_submissions": 2366, + "accepted_block_submissions_delta": 4, + "accepted_block_submissions_rate_per_second": 0.8, + "active_connections": 4, + "authorized_miners": 4, + "backend_main_order": 12198111, + "backend_mineable": 0, + "backend_p2p_mining_fresh": 1, + "backend_peer_lead_blocks": 0, + "backend_submit_ready": 0, + "blocks_found_total": 2396, + "expired_miners": 0, + "invalidated_miners": 0, + "last_accepted_block_submission_age_seconds": 0, + "last_accepted_block_submission_at_epoch": 1782077585, + "ready_miners": 4, + "rejected_block_submissions": 30, + "shares_accepted_total": 3673, + "shares_rejected_total": 1106, + "stale_miners": 0, + "template_age_seconds": 1.7690000000000001 + }, + "pool_health": { + "accepted_block_count": "2366", + "block_submit_error_count": "30", + "block_submit_success_count": "2366", + "source_job_health": { + "authorized_miners": "4", + "ready_miners": "4" + }, + "stale_jobs": false, + "valid_share_count": "3673" + }, + "miner_health_counts": { + "managed_count": 4, + "connected_count": 4, + "configured_count": 4, + "tracked_count": 4, + "identity_contract": "mac-only", + "source": "redis-asic-status" + } + }, + "dashboard_healthz": { + "global_age_seconds": 3, + "global_chain_latest_block": "11831670", + "global_latest_block": "11831670", + "global_status": "ok", + "global_stream_lag_blocks": "0", + "redis": true, + "status": "ok" + }, + "pool_metric_lines": { + "pool_block_submit_outcomes_total": [ + "pool_block_submit_outcomes_total{outcome=\"accepted\",pool_id=\"0\",reason=\"ok\"} 2370", + "pool_block_submit_outcomes_total{outcome=\"rejected\",pool_id=\"0\",reason=\"node-syncing\"} 30", + "pool_block_submit_outcomes_total{outcome=\"rejected-local\",pool_id=\"0\",reason=\"duplicate-block\"} 2", + "pool_block_submit_outcomes_total{outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\"} 1", + "pool_block_submit_outcomes_total{outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 122", + "pool_block_submit_outcomes_total{outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\"} 17" + ], + "pool_job_health_authorized_miners": [ + "pool_job_health_authorized_miners{pool_id=\"0\"} 4" + ], + "pool_job_health_current_job_expired_miners": [ + "pool_job_health_current_job_expired_miners{pool_id=\"0\"} 0" + ], + "pool_job_health_current_job_invalidated_miners": [ + "pool_job_health_current_job_invalidated_miners{pool_id=\"0\"} 0" + ], + "pool_job_health_current_job_stale_miners": [ + "pool_job_health_current_job_stale_miners{pool_id=\"0\"} 0" + ], + "pool_job_health_ok": [ + "pool_job_health_ok{pool_id=\"0\"} 1" + ], + "pool_job_health_ready_miners": [ + "pool_job_health_ready_miners{pool_id=\"0\"} 4" + ], + "pool_rpc_backend_node_health_mineable": [ + "pool_rpc_backend_node_health_mineable{node=\"node\",pool_id=\"0\"} 0" + ], + "pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count": [ + "pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count{node=\"node\",pool_id=\"0\"} 11" + ], + "pool_rpc_backend_node_health_p2p_mining_fresh": [ + "pool_rpc_backend_node_health_p2p_mining_fresh{node=\"node\",pool_id=\"0\"} 1" + ], + "pool_rpc_backend_node_health_submit_ready": [ + "pool_rpc_backend_node_health_submit_ready{node=\"node\",pool_id=\"0\"} 0" + ], + "pool_rpc_backend_node_health_template_age_seconds": [ + "pool_rpc_backend_node_health_template_age_seconds{node=\"node\",pool_id=\"0\"} 1.694" + ], + "pool_rpc_backend_node_health_template_coinbase_valid": [ + "pool_rpc_backend_node_health_template_coinbase_valid{node=\"node\",pool_id=\"0\"} 1" + ], + "pool_shares_accepted_total": [ + "pool_shares_accepted_total{pool_id=\"0\"} 3676" + ], + "pool_shares_rejected_total": [ + "pool_shares_rejected_total{pool_id=\"0\",reason=\"duplicate_block_global\"} 2", + "pool_shares_rejected_total{pool_id=\"0\",reason=\"duplicate_share\"} 1", + "pool_shares_rejected_total{pool_id=\"0\",reason=\"invalidated_job\"} 759", + "pool_shares_rejected_total{pool_id=\"0\",reason=\"non_current_job\"} 204", + "pool_shares_rejected_total{pool_id=\"0\",reason=\"stale_block_candidate\"} 140" + ], + "pool_template_conversion_stall_failure_ratio": [ + "pool_template_conversion_stall_failure_ratio{pool_id=\"0\"} 8.928571428571429" + ] + }, + "asic_performance_summary": { + "age_seconds": 4, + "generated_at_unix_ms": 1782077585880, + "lanes_count": 4, + "ready_count": 4, + "blocks_30m_total": 601, + "lanes_detail": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "remote_host": "192.168.1.101", + "ready": true, + "health": "ok", + "current_job_age_ms": 1336, + "blocks_30m": 143, + "blocks_1h": 346, + "blocks_24h": 623, + "rejected_30m": 7, + "rejected_local_30m": 7, + "protocol_variant": "stratum-v1-basic", + "last_accepted_unix_ms": 1782077584508, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "remote_host": "192.168.1.14", + "ready": true, + "health": "ok", + "current_job_age_ms": 1336, + "blocks_30m": 159, + "blocks_1h": 328, + "blocks_24h": 598, + "rejected_30m": 2, + "rejected_local_30m": 11, + "protocol_variant": "stratum-v1-basic", + "last_accepted_unix_ms": 1782077575932, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "remote_host": "192.168.1.105", + "ready": true, + "health": "ok", + "current_job_age_ms": 1336, + "blocks_30m": 145, + "blocks_1h": 332, + "blocks_24h": 555, + "rejected_30m": 3, + "rejected_local_30m": 7, + "protocol_variant": "stratum-v1-basic", + "last_accepted_unix_ms": 1782077582865, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "remote_host": "192.168.1.102", + "ready": true, + "health": "ok", + "current_job_age_ms": 1336, + "blocks_30m": 154, + "blocks_1h": 344, + "blocks_24h": 590, + "rejected_30m": 2, + "rejected_local_30m": 11, + "protocol_variant": "stratum-v1-basic", + "last_accepted_unix_ms": 1782077581937, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + } + ] + }, + "pre_savepoint_observation": "A transient 23:31 SAST drop invalidated four jobs after node-syncing/parent invalidation, then recovered without code or service changes; this is the failure mode the upgrade must harden." + }, + "non_regression_contract": { + "must_preserve": [ + "live node startup uses Blockdag and miner modules only; peer multiaddrs are allowed, the separate P2P node module is not", + "dashboard mode remains mining when native node and pool are healthy", + "can_accept_shares and can_submit_blocks stay true in healthy state", + "fresh consensus peers >= 2 and p2p_mining_fresh=true before mining is considered safe", + "four ASIC lanes remain MAC-identified, connected, ready, and health=ok", + "zero/missing/mismatched coinbase templates never reach ASIC jobs or block submit path", + "watchdog must not restart node for advisory EVM sync when native mining proof is current and recent accepted work exists" + ], + "minimum_live_gates": { + "dashboard_can_accept_shares": true, + "dashboard_can_submit_blocks": true, + "node_p2p_fresh_consensus_peer_count": 2, + "pool_active_connections": 4, + "pool_ready_miners": 4, + "asic_ready_lanes": 4 + }, + "baseline_snapshot": { + "pool_accepted_block_submissions": 2366, + "pool_ready_miners": 4, + "pool_active_connections": 4, + "asic_blocks_30m_total": 601 + } + }, + "rollback_commands": [ + "cd /home/jeremy/blockdag-source/stack-redis", + "Restore only the changed service first using recorded image/config data.", + "Dashboard rollback: restore recorded dashboard image/config, then docker compose up -d --no-deps --no-build dashboard.", + "Pool rollback: restore recorded pool image/config, then docker compose up -d --no-deps --no-build pool and verify ready_miners=4 plus accepted submits rise.", + "Node rollback: stop pool first; restore recorded node image/config with Blockdag/miner modules only; preserve/quarantine datadir deliberately; start node; verify native health; then start pool." + ] +} diff --git a/ops/savepoints/20260622-000025-pre-node-deploy-retained.json b/ops/savepoints/20260622-000025-pre-node-deploy-retained.json new file mode 100644 index 0000000..ed1b0cd --- /dev/null +++ b/ops/savepoints/20260622-000025-pre-node-deploy-retained.json @@ -0,0 +1,107 @@ +{ + "config_hashes": { + "compose_config_sha256": "f57e0b5ac8d97f9b7468740d5e13d303ad0464978c21c0290c7d9d9fba590154", + "env_sha256_redacted_reference_only": "f46f2c508dec2d79fdf286c5674d01913b80bdc3241aba2d01e06029b7079ff2", + "node_conf_sha256": "d403a9f2b9b9bc1ebd625d3a9b79cdafd8a693b8546455d3b44fce3a4f69af62" + }, + "images": { + "dashboard": { + "candidate_latest_id": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "live_image_id": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6" + }, + "node": { + "candidate_latest_id": "sha256:1fc2c1bc63a9e73aaf7190e571eeb130c96a263f956bdfee84ee67129969c147", + "live_image_id": "sha256:db65d3cd9860cc01e2f241832f2c525299d5140073a8d683ba7ef04c0012ee85" + }, + "pool": { + "candidate_latest_id": "sha256:6c9f9647273e036e8061657ce6c31d8ccc6e4a12ffab6325a2a25e47ce3051f4", + "live_image_id": "sha256:6c9f9647273e036e8061657ce6c31d8ccc6e4a12ffab6325a2a25e47ce3051f4" + }, + "status-sampler": { + "candidate_latest_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "live_image_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739" + } + }, + "live_health": { + "accepted_ok": 38.0, + "active_connections": 4.0, + "authorized_miners": 4, + "can_accept_shares": true, + "can_submit_blocks": true, + "fresh_consensus_peers": 11, + "mode": "mining", + "overall": "ok", + "p2p_mining_fresh": true, + "paid_blocks": 40.0, + "ready_miners": 4, + "template_coinbase_valid": true + }, + "live_node_command": "Path=/usr/local/bin/docker-entrypoint-nodeworker.sh Args=[\"/usr/local/bin/nodeworker\",\"--node-binary=/usr/local/bin/blockdag-node\",\"--node-args=--configfile /etc/bdagStack/node.conf\",\"--rpc-url=ws://127.0.0.1:18546\",\"--dag-rpc-url=http://127.0.0.1:38131\",\"--persist-root=/var/lib/bdagStack/nodeworker\",\"--health-min-peers=2\",\"--rollout-window=30m\"] Entrypoint=[\"/usr/local/bin/docker-entrypoint-nodeworker.sh\",\"/usr/local/bin/nodeworker\",\"--node-binary=/usr/local/bin/blockdag-node\",\"--node-args=--configfile /etc/bdagStack/node.conf\",\"--rpc-url=ws://127.0.0.1:18546\",\"--dag-rpc-url=http://127.0.0.1:38131\",\"--persist-root=/var/lib/bdagStack/nodeworker\",\"--health-min-peers=2\",\"--rollout-window=30m\"]", + "purpose": "Pre-node-deploy rollback point after dashboard/status-sampler and pool candidate deploys recovered to 4 ready miners; retain until post-upgrade savepoint is proven.", + "recorded_at": "2026-06-22T00:00:25+0200", + "rollback_commands": [ + "docker tag sha256:db65d3cd9860cc01e2f241832f2c525299d5140073a8d683ba7ef04c0012ee85 stack-node:latest", + "docker compose up -d --no-deps node", + "docker tag sha256:0f5f66595a3708eea4b084ae1a2c8a9457bb1ae28c9ddfbdf414d87faae0b03d stack-pool:latest", + "docker compose up -d --no-deps pool", + "docker tag sha256:4b04283d589857d4d9e45bf6656dd7293671879ec472a89e86d32af9647c7686 stack-dashboard:latest", + "docker tag sha256:678391c079a5544621ac100e7a3da55a12b90c25d586ba7d4c69b66817b765ff stack-status-sampler:latest", + "docker compose up -d --no-deps dashboard status-sampler" + ], + "safety_invariant": "Live node must use Blockdag and miner modules only, with --miner and non-zero --miningaddr. The separate p2p module remains excluded.", + "savepoint_id": "20260622-000025-pre-node-deploy-retained", + "schema_version": 1, + "source_commits": { + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "head": "33a449269607790e42bc38bcfa3fd213d82ed02e", + "status_short": [ + "M core/json/blocktemplate.go", + " M services/miner/health.go", + " M services/miner/miner_test.go" + ] + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "head": "e02f5b5d68ca62888cbd145fd6928e04c98d3555", + "status_short": [ + "M internal/pool/server/evm_head_guard.go", + " M internal/pool/server/jobBroadcast.go", + " M internal/pool/server/job_broadcast_test.go", + " M internal/pool/server/maturity.go", + " M internal/pool/server/metrics.go", + " M internal/pool/server/node_health.go", + " M internal/pool/server/rpc_router.go", + " M internal/pool/server/rpc_router_test.go" + ] + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "head": "8a28b1817f3e10fe1505be5ba1dab93913cb6400", + "status_short": [ + "M main.go", + " M status_test.go" + ] + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "head": "7b3c812d476bd4d3ef96a9b449de1d692330e40b", + "status_short": [ + "M .env.example", + " M docker-compose.yml", + " M docker/entrypoint-nodeworker.sh", + " M ops/config/stack-defaults.env", + " M ops/pool_ops.py", + " M ops/tests/test_no_miner_collect_status.py", + " M ops/tests/test_nodeworker_entrypoint.py", + " M ops/tests/test_pool_efficiency_loss_ledger.py", + " M ops/tests/test_stack_defaults.py", + " M ops/tests/test_watchdog_sync_restart.py", + " M ops/watchdog.py", + " M scripts/validate-stack-defaults.py", + "?? ops/savepoints/20260621-233309-tonight-baseline-retained.json" + ] + } + }, + "timezone": "Africa/Johannesburg" +} diff --git a/ops/savepoints/20260622-004554-pre-pool-restart-retained.json b/ops/savepoints/20260622-004554-pre-pool-restart-retained.json new file mode 100644 index 0000000..dfd2586 --- /dev/null +++ b/ops/savepoints/20260622-004554-pre-pool-restart-retained.json @@ -0,0 +1,118 @@ +{ + "schema_version": 1, + "savepoint_id": "20260622-004554-pre-pool-restart-retained", + "recorded_at": "2026-06-22T00:45:54+0200", + "timezone": "Africa/Johannesburg", + "purpose": "Pre-pool-build/restart rollback point after patched node recovered to safe mining health and before replacing the stopped pool image.", + "retention": "Retain until post-upgrade working stack is proven and the user explicitly allows cleanup.", + "safety_invariant": "Live node must use Blockdag and miner modules only, with --miner and non-zero --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0. The peer module must remain excluded.", + "live_config": { + "P2P_PORT": "8155", + "BDAG_NODE_MINING_NO_PENDING_TX": "0", + "BDAG_ENABLE_NODE_MINING": "1", + "BDAG_NODE_MODULES": "Blockdag,miner", + "POOL_COINBASE_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "MINING_POOL_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + "compose_hashes": { + "dashboard": "e03f4a8bb3888d03330d9472539c32e0a4893687873b226ce12c92c9412a2162", + "node": "a5728598ce0ac1066702259a5a21222ae332b9f66885287acd8f952fa3e0d257", + "pool": "3cc1bff3269cba209e027936c6f19ed844abc881d2e10bd0111807726ea628d1", + "pool-db": "65ffec4edf19c98fb1bc7919809f6a4a013270b8c2e5878f5528ed49e4002ab1", + "status-sampler": "9d60a4b5400ef9e3431e282622311aaaff140c76375cfffe3ac6613c28f1e091" + }, + "images": { + "node_live": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "node_live_tag": "stack-node:patched-candidate-20260622-0027", + "pool_stopped_before_build": "sha256:6c9f9647273e036e8061657ce6c31d8ccc6e4a12ffab6325a2a25e47ce3051f4", + "pool_rollback_tag": "stack-pool:pre-native-submit-gate-20260622-004554", + "dashboard_live": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "status_sampler_live": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739" + }, + "live_node_command": { + "container": "node", + "status": "running", + "image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "entrypoint": [ + "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ] + }, + "live_pool_state": { + "container": "pool", + "status": "exited", + "image": "sha256:6c9f9647273e036e8061657ce6c31d8ccc6e4a12ffab6325a2a25e47ce3051f4", + "entrypoint": [ + "/usr/local/bin/mining-pool" + ] + }, + "native_template_health": { + "reason_code": "ok", + "mineable_now": true, + "submit_ready": true, + "template_coinbase_valid": true, + "template_coinbase_address": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "p2p_mining_fresh": true, + "p2p_fresh_consensus_peer_count": 14, + "p2p_consensus_peer_count": 15, + "p2p_best_peer_lead_blocks": 0, + "main_order": 12203798 + }, + "source_refs": { + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "head": "e02f5b5d68ca62888cbd145fd6928e04c98d3555", + "dirty_summary": [ + "M internal/pool/server/evm_head_guard.go", + "M internal/pool/server/handler.go", + "M internal/pool/server/jobBroadcast.go", + "M internal/pool/server/job_broadcast_test.go", + "M internal/pool/server/late_submit_policy.go", + "M internal/pool/server/maturity.go", + "M internal/pool/server/metrics.go", + "M internal/pool/server/node_health.go", + "M internal/pool/server/rpc_router.go", + "M internal/pool/server/rpc_router_test.go", + "M internal/pool/server/submit_test.go" + ] + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "head": "7b3c812d476bd4d3ef96a9b449de1d692330e40b", + "dirty_summary": [ + "M .env.example", + "M docker-compose.yml", + "M docker/entrypoint-nodeworker.sh", + "M ops/allow-p2p-iptables.sh", + "M ops/config/stack-defaults.env", + "M ops/install-p2p-services.sh", + "M ops/pool_ops.py", + "M ops/status_sampler.py", + "M ops/systemd/bdag-p2p-firewall.service", + "M ops/tests/test_deployment_portability.py", + "M ops/tests/test_nodeworker_entrypoint.py", + "M ops/tests/test_status_sampler_mining_imperative.py", + "plus prior watchdog/status-sampler safety changes" + ] + } + }, + "validated_before_build": [ + "pool: go test ./... -count=1", + "stack-redis: 191 targeted unittest safety tests", + "stack-redis: release_bootstrap_static_test.py", + "stack-redis: validate-release-build.sh", + "stack-redis: git grep hard p2p module gate returned no matches" + ], + "rollback_commands": [ + "docker tag stack-pool:pre-native-submit-gate-20260622-004554 stack-pool:latest", + "docker compose up -d --no-deps pool", + "If node rollback becomes necessary, stop pool first, restore node image/config deliberately, verify native template health, then start pool." + ] +} diff --git a/ops/savepoints/20260622-005829-post-upgrade-working-stack-retained.json b/ops/savepoints/20260622-005829-post-upgrade-working-stack-retained.json new file mode 100644 index 0000000..62bb429 --- /dev/null +++ b/ops/savepoints/20260622-005829-post-upgrade-working-stack-retained.json @@ -0,0 +1,110 @@ +{ + "schema_version": 1, + "savepoint_id": "20260622-005829-post-upgrade-working-stack-retained", + "recorded_at": "2026-06-22T00:58:29+0200", + "timezone": "Africa/Johannesburg", + "purpose": "Post-upgrade working stack savepoint after native-safe node, pool submit gate, stack safety defaults, dashboard/status visibility changes, and live pool restart were proven mining.", + "retention": "Retain with the original tonight baseline until the user explicitly allows cleanup.", + "safety_invariant": "Live node uses Blockdag and miner modules only, with --miner and non-zero --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0. The peer module remains excluded. Local P2P listen/source port is 8155. BDAG_NODE_MINING_NO_PENDING_TX remains disabled.", + "live_config": { + "P2P_PORT": "8155", + "BDAG_NODE_MINING_NO_PENDING_TX": "0", + "BDAG_ENABLE_NODE_MINING": "1", + "BDAG_NODE_MODULES": "Blockdag,miner", + "POOL_COINBASE_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "MINING_POOL_ADDRESS": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + "images": { + "node": { + "id": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "tags": [ + "stack-node:latest", + "stack-node:patched-candidate-20260622-0027", + "stack-node:post-upgrade-working-20260622-005829" + ] + }, + "pool": { + "id": "sha256:8779aad770f44bbdc1da08fcc151f7856598dabf5eafc35ec020f1d621e3b7cd", + "tags": [ + "stack-pool:latest", + "stack-pool:native-submit-gate-candidate-20260622-0050", + "stack-pool:post-upgrade-working-20260622-005829" + ], + "previous_rollback_tag": "stack-pool:pre-native-submit-gate-20260622-004554" + }, + "dashboard": { + "id": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "tags": [ + "stack-dashboard:latest", + "stack-dashboard:post-upgrade-working-20260622-005829" + ] + }, + "status_sampler": { + "id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "tags": [ + "stack-status-sampler:latest", + "stack-status-sampler:post-upgrade-working-20260622-005829" + ] + } + }, + "live_health": { + "native_reason_code": "ok", + "native_mineable_now": true, + "native_submit_ready": true, + "template_coinbase_valid": true, + "template_coinbase_address": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "p2p_mining_fresh": true, + "fresh_consensus_peers": 18, + "consensus_peers": 19, + "best_peer_lead_blocks": 0, + "main_order": 12204831, + "pool_active_connections": 4, + "pool_backend_healthy": true, + "pool_backend_selected": true, + "pool_backend_submit_ready": true, + "pool_backend_template_coinbase_valid": true + }, + "pool_counters_since_restart": { + "blocks_found_total": 177, + "blocks_submitted_total": 177, + "block_submit_outcome_accepted_ok": 177, + "backend_submit_outcome_accepted_ok": 177, + "blocks_paid_total": 120, + "local_reject_duplicate_block": 2, + "local_reject_stale_job": 10, + "local_reject_stale_parent": 4, + "backend_rejects_observed": 0 + }, + "observations": [ + "The pool reconnected all four ASIC lanes and mined accepted blocks immediately after restart.", + "During transient template-parent-stale/node-syncing periods, stale candidates were rejected locally rather than producing backend reject counters.", + "Same-parent template invalidations preserve current jobs; block-submission parent advances invalidate jobs deliberately.", + "One accepted submit landed near a template-fetch node-syncing race before the refresh path finished invalidating jobs. It was accepted on-chain, but this remains a source refinement target: template-fetch node-syncing should install an immediate submit hold." + ], + "validated_before_live_restart": [ + "pool: go test ./... -count=1", + "stack-redis: 191 targeted unittest safety tests", + "stack-redis: release_bootstrap_static_test.py", + "stack-redis: validate-release-build.sh", + "stack-redis: git diff --check", + "pool: git diff --check", + "stack-redis: hard p2p module grep returned no matches" + ], + "source_refs": { + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "head_before_local_changes": "e02f5b5d68ca62888cbd145fd6928e04c98d3555", + "dirty_state": "contains tested native-health-gated recent-stale submit changes plus earlier upgrade patchset" + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "head_before_local_changes": "7b3c812d476bd4d3ef96a9b449de1d692330e40b", + "dirty_state": "contains tested port 8155, no peer module guard, miningnopending disabled defaults, watchdog/status/pool visibility changes, and retained savepoint manifests" + } + }, + "rollback_commands": [ + "To return to this savepoint: docker tag stack-pool:post-upgrade-working-20260622-005829 stack-pool:latest && docker tag stack-node:post-upgrade-working-20260622-005829 stack-node:latest && docker compose up -d --no-deps node && docker compose up -d --no-deps pool", + "To roll pool only back to pre-submit-gate image: docker compose stop pool && docker tag stack-pool:pre-native-submit-gate-20260622-004554 stack-pool:latest && docker compose up -d --no-deps pool", + "If node rollback becomes necessary, stop pool first, restore node image/config deliberately, verify native template health, then start pool." + ] +} diff --git a/ops/savepoints/20260622-012730-pre-dashboard-pool-deploy-retained.json b/ops/savepoints/20260622-012730-pre-dashboard-pool-deploy-retained.json new file mode 100644 index 0000000..9f37330 --- /dev/null +++ b/ops/savepoints/20260622-012730-pre-dashboard-pool-deploy-retained.json @@ -0,0 +1,7295 @@ +{ + "container_state": { + "dashboard": { + "image_latest_id": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/dashboard" + ], + "Env": [ + "BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS=10", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_TREND_RECENT_WARMUP_BLOCKS=3600", + "POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp", + "DASHBOARD_TIMEZONE=Africa/Johannesburg", + "NODE_RPC_USER=test", + "BDAG_MINER_CONTROL_ENABLED=0", + "BDAG_EVM_WS_URL=ws://host.docker.internal:18546", + "BDAG_REDIS_MAXMEMORY_POLICY=noeviction", + "BDAG_TREND_BACKFILL_SLEEP_MS=100", + "BDAG_CHAIN_WS_READ_LIMIT_BYTES=1048576", + "BDAG_GLOBAL_RPC_WORKERS=4", + "BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS=3", + "BDAG_REDIS_CONFIG=/etc/redis/redis.conf", + "BDAG_REDIS_SOCKET=/run/dashboard-redis/redis.sock", + "BDAG_DASHBOARD_REDIS_ENABLED=1", + "BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30", + "BDAG_GLOBAL_BLOCK_WINDOW=600", + "BDAG_GLOBAL_MAX_INCREMENTAL_GAP=120", + "BDAG_REDIS_APPENDONLY=yes", + "BDAG_DASHBOARD_HEALTH_STALE_SECONDS=20", + "ADDR=0.0.0.0:8088", + "BDAG_CHAIN_POLL_SECONDS=2", + "BDAG_DASHBOARD_REDIS_MANAGED=1", + "BDAG_POOL_METRICS_URL=http://host.docker.internal:9090/metrics", + "BDAG_NODE_RPC_URL=http://host.docker.internal:38131", + "BDAG_TREND_BACKFILL_DAYS=30", + "BDAG_GLOBAL_RPC_CHUNK_SIZE=64", + "BDAG_GLOBAL_SETTLE_DELAY_SECONDS=5", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS=", + "NODE_RPC_PASS=", + "BDAG_TREND_HISTORY_ENABLED=1", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "TMPDIR=/tmp", + "BDAG_TREND_SPARSE_SAMPLES_PER_PASS=", + "BDAG_EVM_HTTP_URL=http://host.docker.internal:18545", + "BDAG_REDIS_MAXMEMORY=256mb", + "BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS=2048", + "BDAG_REDIS_APPENDFSYNC=everysec", + "TEMP=/tmp", + "TMP=/tmp", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "BDAG_EVM_SYNC_BACKOFF_SECONDS=60", + "BDAG_TREND_RPC_WORKERS=4", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_TREND_BACKFILL_CHUNK_SIZE=128", + "BDAG_TREND_CATCHUP_INTERVAL_SECONDS=30", + "BDAG_POOL_URL=stratum+tcp://192.168.1.100:3334", + "BDAG_GLOBAL_FAST_WARMUP_BLOCKS=120", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "8088/tcp": {} + }, + "Healthcheck": { + "Interval": 10000000000, + "Retries": 12, + "Test": [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:8088/ >/dev/null" + ], + "Timeout": 5000000000 + }, + "Hostname": "b9fe4fd94b42", + "Image": "stack-dashboard", + "Labels": { + "com.docker.compose.config-hash": "e03f4a8bb3888d03330d9472539c32e0a4893687873b226ce12c92c9412a2162", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "dashboard", + "com.docker.compose.service": "dashboard", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "" + }, + "Created": "2026-06-21T21:55:34.962441399Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "stack_dashboard-redis:/var/lib/dashboard-redis:rw", + "/proc/net/arp:/host/proc/net/arp:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/run/dashboard-redis": "size=16m,mode=0700", + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/b9fe4fd94b429817d330bf69f269f395b15396ae3cfafc2037837d0d19cf0d52/hostname", + "HostsPath": "/var/lib/docker/containers/b9fe4fd94b429817d330bf69f269f395b15396ae3cfafc2037837d0d19cf0d52/hosts", + "Id": "b9fe4fd94b429817d330bf69f269f395b15396ae3cfafc2037837d0d19cf0d52", + "Image": "sha256:ade262780dbc75954ace95ff0f88c8eccc702453127e898bb59e2b2c7709afb6", + "ImageManifestDescriptor": { + "digest": "sha256:8c6e53f0796e27d4e9991069b354bbaa67a032da41ad6b5800a4cbb001ed2018", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 1242 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/lib/dashboard-redis", + "Driver": "local", + "Mode": "rw", + "Name": "stack_dashboard-redis", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_dashboard-redis/_data", + "Type": "volume" + }, + { + "Destination": "/host/proc/net/arp", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/proc/net/arp", + "Type": "bind" + } + ], + "Name": "/dashboard", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "dashboard", + "dashboard" + ], + "DNSNames": [ + "dashboard", + "b9fe4fd94b42" + ], + "DriverOpts": null, + "EndpointID": "b1bcfce6b7ba1727433667a2c2e7c286731a435664e9d097af372d0cd450bfc3", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.4", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "c6:1d:8f:b9:8c:21", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "SandboxID": "6f9f97f8edf146e8ac31546cd874f8be9fbc79ba51cc14ac62fc1c1504cd78f1", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/dashboard", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/b9fe4fd94b429817d330bf69f269f395b15396ae3cfafc2037837d0d19cf0d52/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "Health": { + "FailingStreak": 0, + "Log": [ + { + "End": "2026-06-22T01:27:46.579363601+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:27:46.524198233+02:00" + }, + { + "End": "2026-06-22T01:27:56.636521663+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:27:56.580788692+02:00" + }, + { + "End": "2026-06-22T01:28:06.700144343+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:28:06.637469811+02:00" + }, + { + "End": "2026-06-22T01:28:16.765826456+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:28:16.701272828+02:00" + }, + { + "End": "2026-06-22T01:28:26.832295408+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:28:26.766729299+02:00" + } + ], + "Status": "healthy" + }, + "OOMKilled": false, + "Paused": false, + "Pid": 3470161, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T21:55:45.549930914Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026/06/21 22:51:10 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:15 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:15 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:20 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:20 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:25 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:25 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:30 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:30 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:35 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:35 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:40 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:40 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:45 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:45 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:50 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:50 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:55 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:51:55 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:00 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:00 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:05 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:05 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:10 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:10 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:15 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:15 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:20 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:20 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:25 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:25 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:30 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:30 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:35 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:35 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:40 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:40 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:45 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:45 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:50 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:50 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:55 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:52:55 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:00 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:00 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:05 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:05 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:10 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:10 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:15 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:15 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:20 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:20 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:25 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:25 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:30 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:30 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:35 pool metrics refresh failed: Get \"http://host.docker.internal:9090/metrics\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:53:35 pool ASIC performance refresh failed: Get \"http://host.docker.internal:9090/health/asic-performance\": dial tcp 172.17.0.1:9090: connect: connection refused\n2026/06/21 22:54:02 global Redis window detected reorg near height 11776763; rebuilding rolling window\n14:M 21 Jun 2026 22:57:47.000 * Starting automatic rewriting of AOF on 1898% growth\n14:M 21 Jun 2026 22:57:47.040 * Creating AOF incr file appendonly.aof.132.incr.aof on background rewrite\n14:M 21 Jun 2026 22:57:47.042 * Background append only file rewriting started by pid 2602\n2602:C 21 Jun 2026 22:57:47.096 * Successfully created the temporary AOF base file temp-rewriteaof-bg-2602.aof\n2602:C 21 Jun 2026 22:57:47.097 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n14:M 21 Jun 2026 22:57:47.143 * Background AOF rewrite terminated with success\n14:M 21 Jun 2026 22:57:47.143 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-2602.aof into appendonly.aof.132.base.rdb\n14:M 21 Jun 2026 22:57:47.185 * Removing the history file appendonly.aof.131.incr.aof in the background\n14:M 21 Jun 2026 22:57:47.185 * Removing the history file appendonly.aof.131.base.rdb in the background\n14:M 21 Jun 2026 22:57:47.215 * Background AOF rewrite finished successfully\n2026/06/21 23:09:13 chain websocket ingest stopped: read tcp 172.18.0.4:55814->172.17.0.1:18546: i/o timeout\n2026/06/21 23:09:27 latest block poll failed: no retrievable EVM block at or below eth_blockNumber 11838611 within 16 blocks: eth_getBlockByNumber(0xb4a484) returned null; eth_getBlockByNumber(latest) fallback failed: EVM RPC eth_getBlockByNumber error -32000: Post \"http://2.24.193.218:18545\": dial tcp 2.24.193.218:18545: i/o timeout\n2026/06/21 23:09:28 chain websocket ingest stopped: read tcp 172.18.0.4:43000->172.17.0.1:18546: i/o timeout\n2026/06/21 23:09:43 chain websocket ingest stopped: read tcp 172.18.0.4:38048->172.17.0.1:18546: i/o timeout\n2026/06/21 23:09:58 chain websocket ingest stopped: read tcp 172.18.0.4:47518->172.17.0.1:18546: i/o timeout\n2026/06/21 23:10:13 chain websocket ingest stopped: read tcp 172.18.0.4:45078->172.17.0.1:18546: i/o timeout\n2026/06/21 23:25:37 global Redis window detected reorg near height 11778851; rebuilding rolling window\n2026/06/21 23:27:39 chain websocket ingest stopped: read tcp 172.18.0.4:44864->172.17.0.1:18546: i/o timeout\n2026/06/21 23:27:54 chain websocket ingest stopped: read tcp 172.18.0.4:41084->172.17.0.1:18546: i/o timeout\n2026/06/21 23:28:09 chain websocket ingest stopped: read tcp 172.18.0.4:40576->172.17.0.1:18546: i/o timeout", + "ports": "8088/tcp -> 127.0.0.1:8088" + }, + "node": { + "image_latest_id": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Env": [ + "BDAG_ENTRYPOINT_CHOWN_MODE=needed", + "BDAG_NODE_MINING_NO_PENDING_TX=0", + "TEMP=/tmp", + "BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "TMP=/tmp", + "BDAG_NODE_MODULES=Blockdag,miner", + "MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m", + "BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc", + "SYNC_SOURCE_NODE=0", + "BDAG_NODE_ARCHIVAL=0", + "BDAG_SNAPSHOT_URL=", + "P2P_PORT=8155", + "BDAG_STORAGE_PROFILE=auto", + "BDAG_DETECTED_NETWORK_TOPOLOGY=", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_ENABLE_NODE_MINING=1", + "BDAG_NODE_NO_FILE_LOGGING=1", + "BDAG_NETWORK_TOPOLOGY=auto", + "BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP", + "BDAG_P2P_ADVERTISE_IP=", + "BDAG_P2P_INTERFACE=", + "BDAG_NODE_MINING_ARGS=", + "TMPDIR=/tmp", + "BDAG_NODE_OBSOLETE_HEIGHT=20", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODE_DEBUG_LEVEL=debug", + "BDAG_NETWORK=", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "18545/tcp": {}, + "18546/tcp": {}, + "38131/tcp": {}, + "38132/tcp": {}, + "6060/tcp": {}, + "8150/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-node", + "Labels": { + "com.docker.compose.config-hash": "cd2bed17cbd5f264367a3668eb1187c214cd03132542f8169834b50d2038d943", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "node", + "com.docker.compose.service": "node", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/node" + }, + "Created": "2026-06-21T22:27:14.249227789Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/home/jeremy/blockdag-source/stack-redis/node.conf:/etc/bdagStack/node.conf:ro", + "/home/jeremy/blockdag-source/stack/data/node:/var/lib/bdagStack/node:rw", + "stack_nodeworker-data:/var/lib/bdagStack/nodeworker:rw", + "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker:/snapshot/latest.bdsnap:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 4096, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -900, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 536870912, + "Tmpfs": { + "/run/bdag-ephemeral": "size=512m,mode=1777", + "/tmp": "size=512m,mode=1777", + "/var/tmp": "size=512m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hostname", + "HostsPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hosts", + "Id": "98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e", + "Image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "ImageManifestDescriptor": { + "digest": "sha256:7ffcf30b8a85bad3b77a4437afbd2c45d249c87a1fed5f0b6203dd2a6b5ffc6e", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2564 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/snapshot/latest.bdsnap", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker", + "Type": "bind" + }, + { + "Destination": "/etc/bdagStack/node.conf", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "Type": "bind" + }, + { + "Destination": "/var/lib/bdagStack/node", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack/data/node", + "Type": "bind" + }, + { + "Destination": "/var/lib/bdagStack/nodeworker", + "Driver": "local", + "Mode": "rw", + "Name": "stack_nodeworker-data", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_nodeworker-data/_data", + "Type": "volume" + } + ], + "Name": "/node", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "6a9346674bfadb8c19a03ff52c78803f32a10c9ad091efa7164f99827c3e3fb2", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "747be80cabc6764f4fc40a1e1dcdd2f581cbcbb8c329b64bf5e46e74c31dd0d2", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3534321, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T22:27:16.468444346Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b05100..d1c3aa from=0x9F1158F81E2EaDc0Ce0D490eE64703207DA9dE3e err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ab6640..14d043 from=0x20d7D512aC4C885b12145f47c653fb5bd1f1B3dC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=493e91..23c3d4 from=0xc2e0E98Cb12F919ae1206faAc2C5a856aE6c7Cb2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a2c1b5..1d766a from=0x769656a9A24483bEb75d85574130DF6490853687 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=07a2ca..cd2c27 from=0xAC02a613C816d53e97331ec8E6c11eC787199A7d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a83d2c..b7a451 from=0x6FF39356A422CE91e278F0aB024DCb81D842fd93 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ac17c0..102fe0 from=0x4a2b631957c48e9E3b6A50d623e0A9E16325b453 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1b0ec4..1bb2a8 from=0xA6Bc27Dd5C3b5f67a3f8272854E37Cc79Bf66138 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b14e0e..d4899e from=0xbE51a3eaD791FAd33C6D8D648529b559Aed54597 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=e1dd19..16fa85 from=0x4813d5f4792B85b33BB8b58BA1e74c39e7A07976 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=04ae0a..dce647 from=0x2bfcB88ad3f71b6279d2ee0eFa4B215A9e5B44aA err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6d514b..7efba1 from=0x958ed7317d7a12D9211d29c60077B3f14EC76837 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3cb531..dcfc5c from=0xC0c7082379E68004A9746CE3a5F1768ec8DEd33F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5dd78b..5fae00 from=0x8fc52379b14a43CB9cAD764D93627D7DDaa30795 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1ae958..f91920 from=0x157f687727A7cDE9c674dEb7e5b2b047ABE51Ec4 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7e8293..c113ac from=0x93b2E311805D6c6C4A83b10D4CB4302ae79663Fb err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=74bd26..3e1555 from=0xB64F7Eb749166696A27182dFa48B1806Bc98Ce99 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:28:30.166 [DEBUG] Update BdagPool module=BDAG number=11839952 txs=0 gas=0 fees=0 elapsed=6.494ms\n2026-06-21|23:28:30.166 [INFO ] update BdagPool snapshot module=BDAG size=0\n2026-06-21|23:28:30.167 [DEBUG] Auto-staked 7330380572400000000 BDAG (minted 384017348461961 Shares) for 0x1719E0ee598c15957448D5E568948101DF78e7A0 in Epoch 19. module=bdag/consensus\n2026-06-21|23:28:30.167 [DEBUG] Template parent changed module=miner old=0xf472b42611497a... new=0x9af6927074667c... reason=parent_changed\n2026-06-21|23:28:30.167 [DEBUG] gbtdetail module=miner txcount=0 parents=\"[{Data:9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab Hash:0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab}]\" prehash=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab longpollid=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab-1782084510\n2026-06-21|23:28:30.167 [DEBUG] gbtend module=miner txcount=0 longpollid=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab-1782084510 spent=0\n2026-06-21|23:28:30.167 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:28:30.167 [DEBUG] gbtdetail module=miner txcount=0 parents=\"[{Data:9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab Hash:0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab}]\" prehash=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab longpollid=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab-1782084510\n2026-06-21|23:28:30.167 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=333.217161ms\n2026-06-21|23:28:30.167 [DEBUG] gbtend module=miner txcount=0 longpollid=0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab-1782084510 spent=0\n2026-06-21|23:28:30.167 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:28:30.167 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=6.608568ms\n2026-06-21|23:28:30.230 [DEBUG] Accepted block module=CHAIN hash=0x2fbc6a37ac79ee731af7da5eef5547231debc8723ce9187241ff99e8ed380710 age=1.230220516s source=\"\"\n2026-06-21|23:28:30.437 [DEBUG] request handled module=gateway method=eth_getBlockByNumber target=5.78.193.245:18545 duration=604.128765ms\n2026-06-21|23:28:30.437 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:30.438 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:30.438 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:30.571 [DEBUG] request handled module=gateway method=eth_getTransactionCount target=5.78.193.245:18545 duration=410.38448ms\n2026-06-21|23:28:30.572 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:30.572 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:30.573 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:30.573 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.573 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.574 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.577 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.594 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.594 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.716 [DEBUG] request handled module=gateway method=eth_getBlockByNumber target=5.78.193.245:18545 duration=278.420557ms\n2026-06-21|23:28:30.717 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:30.717 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:30.717 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:30.717 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.726 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:30.726 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:30.726 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:30.749 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.765 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.788 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.840 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.846 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.849 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.850 [DEBUG] request handled module=gateway method=eth_gasPrice target=5.78.193.245:18545 duration=277.899262ms\n2026-06-21|23:28:30.851 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:30.851 [DEBUG] using sender affinity module=gateway from=0x1719E0ee598c15957448D5E568948101DF78e7A0 key=\n2026-06-21|23:28:30.852 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:30.852 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:30.854 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.857 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.860 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:30.995 [DEBUG] request handled module=gateway method=eth_getBlockByNumber target=5.78.193.245:18545 duration=278.573416ms\n2026-06-21|23:28:31.002 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:31.003 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:31.003 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545\n2026-06-21|23:28:31.003 [DEBUG] request handled module=gateway method=eth_getBlockByNumber target=5.78.193.245:18545 duration=277.578162ms\n2026-06-21|23:28:31.098 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:31.099 [DEBUG] Updating sync peer module=SYNC force=false\n2026-06-21|23:28:31.130 [DEBUG] request handled module=gateway method=eth_sendRawTransaction target=5.78.193.245:18545 duration=279.245618ms\n2026-06-21|23:28:31.166 [DEBUG] Served eth_getBalance \u001b[0mconn\u001b[0m=127.0.0.1:38964 \u001b[0mreqid\u001b[0m=2882 \u001b[0mduration\u001b[0m=\"131.877\u00b5s\"\n2026-06-21|23:28:31.166 [DEBUG] request handled module=gateway method=eth_getBalance target= duration=354.257\u00b5s\n2026-06-21|23:28:31.167 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11839951 maxHeight=11857877\n2026-06-21|23:28:31.167 [DEBUG] using sender affinity for nonce module=gateway from=0x1719e0ee598c15957448d5e568948101df78e7a0 key=\n2026-06-21|23:28:31.167 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779039 latency=1.10639043s synced=true active=true\n2026-06-21|23:28:31.167 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545", + "ports": "" + }, + "pool": { + "image_latest_id": "sha256:8779aad770f44bbdc1da08fcc151f7856598dabf5eafc35ec020f1d621e3b7cd", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/mining-pool" + ], + "Env": [ + "POOL_BLOCK_TIMING_CONTROLLER_STEP_MS=250", + "POOL_FEE_PERCENTAGE=1.0", + "POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT=5", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "TMP=/tmp", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30", + "POOL_INSTANCE_ID=", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_PRIVATE_KEY=", + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0", + "POOL_BLOCK_MATURITY=10", + "NODE_RPC_URLS=http://127.0.0.1:38131", + "POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000", + "TMPDIR=/tmp", + "PG_URL=postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable", + "TEMP=/tmp", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true", + "PPLNS_N_WORK=1000", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15", + "POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000", + "POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_RPC_BACKENDS=", + "POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0", + "POOL_RUNTIME_ADMIN_ENABLED=false", + "POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS=60", + "POOL_TEMPLATE_TTL_REFRESH_MS=100", + "POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS=20", + "POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false", + "POOL_BLOCK_TIMING_CONTROLLER_ENABLED=true", + "METRICS_ADDR=0.0.0.0:9090", + "POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false", + "NODE_RPC_USER=test", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2", + "POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true", + "POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100", + "POOL_STALE_RACE_REJECT_WINDOW_SECONDS=10", + "POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES=20", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "POOL_BIND_ADDR=192.168.1.100:3334", + "POOL_SUBMIT_RPC_URLS=", + "NODE_RPC_PASS=", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3", + "POOL_PAYOUT_MATURITY=4096", + "NODE_RPC_URL=http://127.0.0.1:38131", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750", + "POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750", + "WALLET_RPC_URL=http://127.0.0.1:18545", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false", + "POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750", + "POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "3334/tcp": {}, + "8080/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-pool", + "Labels": { + "com.docker.compose.config-hash": "3cc1bff3269cba209e027936c6f19ed844abc881d2e10bd0111807726ea628d1", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:8779aad770f44bbdc1da08fcc151f7856598dabf5eafc35ec020f1d621e3b7cd", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "pool", + "com.docker.compose.service": "pool", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "bdagStack", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/pool" + }, + "Created": "2026-06-21T22:53:38.391997075Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 3072, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -800, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/5bb5024a7efa46bc46c948f3e78b0c1e917ae2e5179896255e5102fc1cece892/hostname", + "HostsPath": "/var/lib/docker/containers/5bb5024a7efa46bc46c948f3e78b0c1e917ae2e5179896255e5102fc1cece892/hosts", + "Id": "5bb5024a7efa46bc46c948f3e78b0c1e917ae2e5179896255e5102fc1cece892", + "Image": "sha256:8779aad770f44bbdc1da08fcc151f7856598dabf5eafc35ec020f1d621e3b7cd", + "ImageManifestDescriptor": { + "digest": "sha256:12a69f6eb94189dc596a17a26ff7a1f4416c8d7e49addf7a0524afb2fdcafd11", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2000 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [], + "Name": "/pool", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "e26bb7aa236a1f120ddde6174f7b41d788535f742626381b2836e78e02b421f1", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "fc0f34432fc3ed728781437d101dedbe4ad8f44267b6eaf9ada5341438e1b4b2", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/mining-pool", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/5bb5024a7efa46bc46c948f3e78b0c1e917ae2e5179896255e5102fc1cece892/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3573172, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T22:53:38.552512992Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026/06/21 23:27:37 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_12000000 extranonce2=00000000 ntime=6a387360 nonce=5fa08dc9 suppressed=7\n2026/06/21 23:27:37 \ud83c\udfaf BLOCK FOUND height(le)=9347505 job=2825-18bb3c5d6cc90c8e_12000000 hash=c9c83a012bc4fe42835a9c192d572f9cd71f476c984e411cca66cb7201000000 target=02e55d0000000000000000000000000000000000000000000000000000\n2026/06/21 23:27:37 \u26a0\ufe0f Block submission backend not ready backend=node reason=node-syncing: structured submit rejected: code=node_syncing message=Client in initial download : Blockdag is downloading blocks: node is still syncing with peers\n2026/06/21 23:27:38 [REFRESH] invalidated 4 current miner jobs by epoch reason=node health p2p unready: peer-lead-exceeds-tolerance\n2026/06/21 23:27:38 [ROUTER] invalidated 4 accepted jobs for backend=node reason=node health p2p unready: peer-lead-exceeds-tolerance\n2026/06/21 23:27:38 [REFRESH] invalidated 4 current miner jobs by epoch reason=node template invalidation: parent_changed\n2026/06/21 23:27:38 [ROUTER] invalidated 4 accepted jobs for backend=node reason=node template invalidation: parent_changed\n2026/06/21 23:27:38 template fetch error: getBlockTemplate paused: node-health-node-syncing (transient=node-syncing suppressed=6)\n2026/06/21 23:27:38 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:27:38 [REFRESH] template refresh backend not ready: node-syncing current_jobs_invalidated=4 (seq=2825 parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 suppressed=1)\n2026/06/21 23:27:38 Payout: deferred payout processing because backend is not mining-safe reason=node-health-node-syncing suppressed=3\n2026/06/21 23:27:39 [REFRESH] template pressure: stale-job (seq=2825 parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499)\n2026/06/21 23:27:39 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:27:39 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_14000000 reason=stale-job backend=node job_age=10.178s template_seq=2825 current_seq=2825 job_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 current_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 hash=a7f6f0c1b7ef8fcbb091441dd33fd67cb77f3e3f41f7dea57f1de10102000000\n2026/06/21 23:27:39 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_13000000 reason=stale-job backend=node job_age=10.281s template_seq=2825 current_seq=2825 job_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 current_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 hash=a70b83f24ac0f3ae87b550716a7aaf696b0c3cda9e5d02624be022d101000000\n2026/06/21 23:27:43 [STRATUM] accepted client addr=192.168.1.105:50821 host=192.168.1.105 lane=mac:28:e2:97:4d:44:3a mac=28:e2:97:4d:44:3a suppressed=19\n2026/06/21 23:27:43 [STRATUM] client closed addr=192.168.1.105:50821 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=19\n2026/06/21 23:27:45 [vardiff DEBUG] hold pdiff 0.162880 (shares=17 expired=0 in 60s, ratio=1.20, lower=0.50 upper=1.50)\n2026/06/21 23:27:53 [vardiff DEBUG] hold pdiff 0.204279 (shares=13 expired=3 in 61s, ratio=0.90, lower=0.50 upper=1.50)\n2026/06/21 23:27:56 [vardiff DEBUG] hold pdiff 0.199845 (shares=11 expired=4 in 60s, ratio=0.77, lower=0.50 upper=1.50)\n2026/06/21 23:27:59 \u2705 Maturity: marked 2 blocks MATURE currentHeight=9347548 first=428a2e3d1c9bb135dc37747d376f15e3a7a505d23b3890e1401e369f02000000 last=f4606dae507131c0dab6f67a38c5e3a37441cd39d7a26be3907a8e9102000000 minConfs=45 maxConfs=45\n2026/06/21 23:27:59 [STRATUM] accepted client addr=192.168.1.14:40045 host=192.168.1.14 lane=mac:28:e2:97:3e:39:63 mac=28:e2:97:3e:39:63 suppressed=17\n2026/06/21 23:27:59 [STRATUM] client closed addr=192.168.1.14:40045 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=17\n2026/06/21 23:28:00 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_0d000000 extranonce2=00000001 ntime=6a387360 nonce=c012ecb2 suppressed=8\n2026/06/21 23:28:02 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_14000000 extranonce2=00000002 ntime=6a387360 nonce=ff67a6e3 suppressed=8\n2026/06/21 23:28:08 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_12000000 extranonce2=00000002 ntime=6a387360 nonce=cf191edc suppressed=9\n2026/06/21 23:28:08 [REFRESH] invalidated 4 current miner jobs by epoch reason=node health p2p unready: peer-lead-exceeds-tolerance\n2026/06/21 23:28:08 [ROUTER] invalidated 4 accepted jobs for backend=node reason=node health p2p unready: peer-lead-exceeds-tolerance\n2026/06/21 23:28:08 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:28:08 [REFRESH] template refresh backend not ready: node-syncing current_jobs_invalidated=4 (seq=2825 parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 suppressed=1)\n2026/06/21 23:28:09 [TIMING-CONTROL] action=freshen-work reason=stale-submit changed=true waste=22.7% accepted=17 failed=5 total=22 active_miners=4 job_age=1.75s->1.75s ttl=100ms->100ms stale_grace=1.5s->1.25s\n2026/06/21 23:28:09 [REFRESH] template pressure: stale-job (seq=2825 parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 suppressed=1)\n2026/06/21 23:28:09 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:28:09 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_14000000 reason=stale-job backend=node job_age=39.631s template_seq=2825 current_seq=2825 job_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 current_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 hash=01ec79c1880d2e58fb4bc07c0758218bc1533576ba485bf295ad4b8501000000\n2026/06/21 23:28:09 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:28:09 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_12000000 reason=stale-job backend=node job_age=40.145s template_seq=2825 current_seq=2825 job_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 current_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 hash=ba628edbd550b8369dd07df51c366bcc872e3474855e20326d2da16400000000\n2026/06/21 23:28:10 [REFRESH] invalidated 4 current miner jobs by epoch reason=template refresh backend not ready: node-syncing\n2026/06/21 23:28:10 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_0d000000 reason=stale-job backend=node job_age=41.005s template_seq=2825 current_seq=2825 job_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 current_parent=08914503ec4e09e5914e7b8a51e0071597965aa8a7cc0394d84d27e4b673f499 hash=d4a4df3cbf8a017ed7ea3bed3f423fc67dcb0e3686c5f21bf0b25e2001000000\n2026/06/21 23:28:11 \u2705 valid share accepted 21591.104414 \u2192 1414994619 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_14000000 suppressed=0 suppressedDiff=0.000000 suppressedWork=0\n2026/06/21 23:28:12 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2825-18bb3c5d6cc90c8e_13000000 extranonce2=00000002 ntime=6a387360 nonce=2bd2f9c9 suppressed=7\n2026/06/21 23:28:22 [vardiff DEBUG] hold pdiff 0.198793 (shares=4 expired=8 in 60s, ratio=0.28, lower=0.50 upper=1.50)\n2026/06/21 23:28:23 [REFRESH] invalidated 4 current miner jobs by epoch reason=node template invalidation: parent_changed\n2026/06/21 23:28:23 [ROUTER] invalidated 4 accepted jobs for backend=node reason=node template invalidation: parent_changed\n2026/06/21 23:28:23 [REFRESH] node template invalidation: parent_changed (seq=2826 parent=3b0ce4fadc96de7fe19ecccd5e8b809884b56ce405c56160661bd852d7943db0)\n2026/06/21 23:28:23 [REFRESH] invalidated 4 current miner jobs by epoch reason=node template invalidation: parent_changed\n2026/06/21 23:28:23 [REFRESH] clean template refresh invalidated_jobs=4 reason=node template invalidation: parent_changed\n2026/06/21 23:28:24 \u2705 valid share accepted 12562.648572 \u2192 823305737 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2826-18bb3c6a08d5995b_0d000000 suppressed=2 suppressedDiff=32534.923586 suppressedWork=2132208752\n2026/06/21 23:28:24 [REFRESH] ws tip change to a597bbcf3401dd12a5b35b3eb7b272fb3552cd874d39715dcaac623a5ea2dcbb (seq=2827 parent=a597bbcf3401dd12a5b35b3eb7b272fb3552cd874d39715dcaac623a5ea2dcbb suppressed=1)\n2026/06/21 23:28:25 \ud83c\udfaf BLOCK FOUND height(le)=9347567 job=2827-18bb3c6a3bc44465_0d000000 hash=88cef4380f99852403de3cfe1b71789f16e6054c9c6e444b93685b4602000000 target=02e19e0000000000000000000000000000000000000000000000000000\n2026/06/21 23:28:25 \u2705 Block submitted successfully: map[accepted:true block_hash:0x30b3988cfc2cc19d68e229a3f9b4e840ae649678e7a411c73243ee2b1668b9e1 code:accepted coinbase_amount:10 coinbase_txid: height:9.347567e+06 message:accepted miner_type:GBT_Worker order:12207099 template_seq:3391]\n2026/06/21 23:28:25 [REFRESH] accepted block parent advance (seq=2827 parent=30b3988cfc2cc19d68e229a3f9b4e840ae649678e7a411c73243ee2b1668b9e1)\n2026/06/21 23:28:25 \u2705 Block & Credits saved to Database suppressed=8\n2026/06/21 23:28:25 \ud83d\udcb0 CREDIT SUMMARY block=88cef4380f99852403de3cfe1b71789f16e6054c9c6e444b93685b4602000000 height=9347567 totalWork=368605623451 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9347577 payoutUnlockHeight=9351663 suppressedDetailBlocks=0\n2026/06/21 23:28:25 [REFRESH] ws TTL refresh (seq=2828 parent=30b3988cfc2cc19d68e229a3f9b4e840ae649678e7a411c73243ee2b1668b9e1 suppressed=1)\n2026/06/21 23:28:25 [REFRESH] block submission accepted (seq=2829 parent=30b3988cfc2cc19d68e229a3f9b4e840ae649678e7a411c73243ee2b1668b9e1)\n2026/06/21 23:28:25 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:28:25 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:28:26 \u2705 valid share accepted 16591.346345 \u2192 1087330474 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2830-18bb3c6a90e440d4_13000000 suppressed=4 suppressedDiff=82741.040103 suppressedWork=5422516804\n2026/06/21 23:28:27 \u2705 valid share accepted 21828.571210 \u2192 1430557243 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=2831-18bb3c6ab6b1f8a9_12000000 suppressed=3 suppressedDiff=44434.767810 suppressedWork=2912076943\n2026/06/21 23:28:28 \ud83c\udfaf BLOCK FOUND height(le)=9347570 job=2831-18bb3c6ab6b1f8a9_13000000 hash=f395e45b04e8fe3c2809ed294406929409866e3181ee205e4573d3e400000000 target=02e19e0000000000000000000000000000000000000000000000000000\n2026/06/21 23:28:28 \u2705 Block submitted successfully: map[accepted:true block_hash:0x748f329f25beb03664a4fdfa0c880ab0955a236d20fee629c9cd00ca7236f933 code:accepted coinbase_amount:10 coinbase_txid: height:9.34757e+06 message:accepted miner_type:GBT_Worker order:12207102 template_seq:3395]\n2026/06/21 23:28:28 [REFRESH] accepted block parent advance skipped: current parent already superseded (seq=2832 parent=d8251c3fbf57298557fa18813e708fa94912cb46b43b938298880eb578800273 suppressed=1)\n2026/06/21 23:28:28 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:28:28 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:28:28 \ud83d\udcb0 CREDIT SUMMARY block=f395e45b04e8fe3c2809ed294406929409866e3181ee205e4573d3e400000000 height=9347570 totalWork=369376906169 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9347580 payoutUnlockHeight=9351666 suppressedDetailBlocks=0\n2026/06/21 23:28:28 \ud83d\udcb8 Processing Payouts for Block e2e0473fe7f3bd1d888820ad78cb4275696e13634100d369fe6b915a01000000 (1 credits)\n2026/06/21 23:28:29 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:28:29 [REFRESH] ws tip change to f472b42611497af5dde5e2bf705fbfa6b4bee1f39f8a22f408c1522d2e7c73c3 (seq=2835 parent=f472b42611497af5dde5e2bf705fbfa6b4bee1f39f8a22f408c1522d2e7c73c3 suppressed=4)\n2026/06/21 23:28:29 \ud83c\udfaf BLOCK FOUND height(le)=9347572 job=2835-18bb3c6b68bdc1c3_14000000 hash=d2013b2eda593136bb1a70a3026d2ed9e17611ccd622f52c25f4626c00000000 target=02e19e0000000000000000000000000000000000000000000000000000\n2026/06/21 23:28:29 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x4cbc47b02d74389143af6a78008ddee72f644f8b0cfe9914f13beb50788965c7)\n2026/06/21 23:28:29 \ud83d\udcb0 Block e2e0473fe7f3bd1d888820ad78cb4275696e13634100d369fe6b915a01000000 marked as PAID\n2026/06/21 23:28:30 \u2705 Block submitted successfully: map[accepted:true block_hash:0x9af6927074667c8fb3d73f0a1686b8f9ff157e198031d6f8290502c9c1f90cab code:accepted coinbase_amount:10 coinbase_txid: height:9.347572e+06 message:accepted miner_type:GBT_Worker order:12207106 template_seq:3397]\n2026/06/21 23:28:30 \ud83d\udcb8 Processing Payouts for Block 37ca8f65b7d810bc7872b1ab1d101dd806366f18595401c1dde3ac3a01000000 (1 credits)\n2026/06/21 23:28:30 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:28:30 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:28:30 \ud83d\udcb0 CREDIT SUMMARY block=d2013b2eda593136bb1a70a3026d2ed9e17611ccd622f52c25f4626c00000000 height=9347572 totalWork=369775543545 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9347582 payoutUnlockHeight=9351668 suppressedDetailBlocks=0\n2026/06/21 23:28:30 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:28:31 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x151ccddd22b49e51e7ca79f08693d67125c23fef002c00ff80959d8987f59409)\n2026/06/21 23:28:31 \ud83d\udcb0 Block 37ca8f65b7d810bc7872b1ab1d101dd806366f18595401c1dde3ac3a01000000 marked as PAID\n2026/06/21 23:28:31 \ud83d\udcb8 Processing Payouts for Block f3c7f62c023e2587054dbd151dc964e366b94b2da0e60ebf5df25d2b01000000 (1 credits)", + "ports": "" + }, + "status-sampler": { + "image_latest_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/bin/sh", + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Env": [ + "BDAG_STATUS_SAMPLER_FILE=/home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json", + "BDAG_MINING_IMPERATIVE_GUARD_UNITS=", + "BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS=30", + "BDAG_CHAIN_STATE_SELF_HEAL_COMMAND=ops/chain-state-self-heal.sh --from-systemd", + "NODE_RPC_PASS=", + "BDAG_CHAIN_STATE_SELF_HEAL_UNIT=", + "BDAG_NODE_RPC_URLS=node=http://host.docker.internal:38131", + "BDAG_POOL_DB_CONTAINER=postgres", + "BDAG_NODE_SERVICES=node", + "BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60", + "BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL=http://dashboard:8088/api/status", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_POOL_CONTAINERS=pool", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "TMPDIR=/tmp", + "BDAG_POOL_CONTAINER=pool", + "COMPOSE_PROJECT_NAME=stack", + "BDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10", + "NODE_RPC_USER=test", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "Hostname": "dc4efa9d3462", + "Image": "stack-status-sampler", + "Labels": { + "com.docker.compose.config-hash": "9d60a4b5400ef9e3431e282622311aaaff140c76375cfffe3ac6613c28f1e091", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "status-sampler", + "com.docker.compose.service": "status-sampler", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Status Sampler" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "1000:1000", + "Volumes": null, + "WorkingDir": "/home/jeremy/blockdag-source/stack-redis" + }, + "Created": "2026-06-21T21:55:34.980730129Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/var/run/docker.sock:/var/run/docker.sock:rw", + "/home/jeremy/blockdag-source/stack-redis:/home/jeremy/blockdag-source/stack-redis:rw" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 100, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": [ + "115" + ], + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hostname", + "HostsPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hosts", + "Id": "dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757", + "Image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "ImageManifestDescriptor": { + "digest": "sha256:14ad5cf6cca8e3a1be3b533311cc93dc62783a0f8190e9edca3b760a3245c6af", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2572 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/var/run/docker.sock", + "Type": "bind" + }, + { + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Type": "bind" + } + ], + "Name": "/status-sampler", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "status-sampler", + "status-sampler" + ], + "DNSNames": [ + "status-sampler", + "dc4efa9d3462" + ], + "DriverOpts": null, + "EndpointID": "bea9145ad37eec88c0035936c500122a15538bbae3d48da30f659295fb0c2fca", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "4a:58:5e:1c:0a:0e", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": {}, + "SandboxID": "ac7a75b21d14e791247df23bd6b200be7bb07adfe71b74c3c2a21329237a4909", + "SandboxKey": "" + }, + "Path": "/bin/sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3470155, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T21:55:45.539854201Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "", + "ports": "" + } + }, + "created_at_sast": "2026-06-22T01:28:33+0200", + "dashboard_healthz": { + "global_age_seconds": 0, + "global_chain_latest_block": "11779045", + "global_latest_block": "11779045", + "global_status": "ok", + "global_stream_lag_blocks": "0", + "redis": true, + "status": "ok" + }, + "dashboard_live_status": { + "api_error": "", + "asic_rows": [ + { + "blocks_1h": "221", + "blocks_24h": "221", + "blocks_30m": "169", + "health": "ok", + "ip": "192.168.1.101", + "job_age": "776 ms", + "last_block": "Jun 22, 2026 01:27:08 SAST", + "mac": "28:e2:97:1e:c0:b5", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "17" + }, + { + "blocks_1h": "237", + "blocks_24h": "237", + "blocks_30m": "186", + "health": "ok", + "ip": "192.168.1.102", + "job_age": "776 ms", + "last_block": "Jun 22, 2026 01:28:28 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "14" + }, + { + "blocks_1h": "231", + "blocks_24h": "231", + "blocks_30m": "183", + "health": "ok", + "ip": "192.168.1.105", + "job_age": "776 ms", + "last_block": "Jun 22, 2026 01:28:25 SAST", + "mac": "28:e2:97:4d:44:3a", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "15" + }, + { + "blocks_1h": "257", + "blocks_24h": "257", + "blocks_30m": "200", + "health": "ok", + "ip": "192.168.1.14", + "job_age": "776 ms", + "last_block": "Jun 22, 2026 01:28:29 SAST", + "mac": "28:e2:97:3e:39:63", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "19" + } + ], + "chart_json": "[{\"color\":\"#2bd4a3\",\"label\":\"28:e2:97:1e:c0:b5\",\"mac\":\"28:e2:97:1e:c0:b5\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":52,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":67,\"rejected\":3,\"rejected_local\":1,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":54,\"rejected\":0,\"rejected_local\":8,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":48,\"rejected\":1,\"rejected_local\":4,\"t\":\"2026-06-21T23:20:00Z\"}],\"ready\":true},{\"color\":\"#f5c542\",\"label\":\"28:e2:97:3e:39:63\",\"mac\":\"28:e2:97:3e:39:63\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":57,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":65,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":65,\"rejected\":0,\"rejected_local\":8,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":70,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-21T23:20:00Z\"}],\"ready\":true},{\"color\":\"#ff7a7a\",\"label\":\"28:e2:97:4d:44:3a\",\"mac\":\"28:e2:97:4d:44:3a\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":48,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":70,\"rejected\":0,\"rejected_local\":7,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":63,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":50,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-21T23:20:00Z\"}],\"ready\":true},{\"color\":\"#7ca7ff\",\"label\":\"2a:71:c7:f5:1f:1e\",\"mac\":\"2a:71:c7:f5:1f:1e\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":51,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":62,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":67,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":57,\"rejected\":1,\"rejected_local\":4,\"t\":\"2026-06-21T23:20:00Z\"}],\"ready\":true}]", + "evidence": [ + { + "label": "Live Mode", + "value": "mining" + }, + { + "label": "Live Overall", + "value": "ok" + }, + { + "label": "Miner Activity", + "value": "yes" + }, + { + "label": "Accepting Shares", + "value": "yes" + }, + { + "label": "Submit Ready", + "value": "yes" + }, + { + "label": "Getting Fresh Work", + "value": "yes" + }, + { + "label": "Won a block in last 600?", + "value": "yes" + } + ], + "groups": null, + "node": { + "evidence": [ + { + "label": "Node Block Height", + "value": "12,207,107" + }, + { + "label": "Peer Main Order", + "value": "12,207,106" + }, + { + "label": "Peer Order Gap", + "value": "0" + }, + { + "label": "Peers", + "value": "18" + }, + { + "label": "EVM Block Height", + "value": "11,839,951" + } + ], + "state": "Synced", + "status_steps": [ + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "P2P Down" + }, + { + "active": false, + "label": "Syncing" + }, + { + "active": true, + "label": "Synced" + }, + { + "active": false, + "label": "Node Degraded" + } + ], + "title": "Node Status", + "tone": "ok" + }, + "notes": null, + "refresh_every": "5s", + "rows": [ + { + "identity": "28:e2:97:1e:c0:b5", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.101", + "last": "Jun 22, 2026 01:28:15 SAST", + "mac": "28:e2:97:1e:c0:b5", + "observed": "192.168.1.101", + "shares": "0", + "status": "ok" + }, + { + "identity": "2a:71:c7:f5:1f:1e", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.102", + "last": "Jun 22, 2026 01:28:15 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "observed": "192.168.1.102", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:4d:44:3a", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.105", + "last": "Jun 22, 2026 01:28:15 SAST", + "mac": "28:e2:97:4d:44:3a", + "observed": "192.168.1.105", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:3e:39:63", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.14", + "last": "Jun 22, 2026 01:28:15 SAST", + "mac": "28:e2:97:3e:39:63", + "observed": "192.168.1.14", + "shares": "0", + "status": "ok" + } + ], + "secondary": [ + { + "label": "Miner Inventory", + "value": "4" + }, + { + "label": "Valid Shares", + "value": "1972" + }, + { + "label": "Last Share Age", + "value": "not known" + }, + { + "label": "Submit Errors", + "value": "6" + }, + { + "label": "Template Coinbase Valid", + "value": "unknown" + }, + { + "label": "Zero Coinbase Reject Rate", + "value": "unknown" + }, + { + "label": "Ready Miner Jobs", + "value": "4" + }, + { + "label": "Local Stale Ratio", + "value": "unknown" + }, + { + "label": "Last Automation Restart", + "value": "none" + }, + { + "label": "Pool Endpoint", + "value": "192.168.1.100:3334" + } + ], + "state": "Mining", + "state_tone": "ok", + "status_steps": [ + { + "active": false, + "label": "Waiting for Live Data" + }, + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "Waiting for a Miner" + }, + { + "active": false, + "label": "Waiting for Node Sync" + }, + { + "active": false, + "label": "Template Unsafe" + }, + { + "active": false, + "label": "Degraded" + }, + { + "active": true, + "label": "Mining" + } + ], + "title": "Pool Status", + "updated_at": "Jun 22, 2026 01:28:31 SAST", + "warnings": null + }, + "dashboard_status": { + "asic_performance": { + "age_seconds": 1, + "bucket_seconds": 600, + "generated_at_unix_ms": 1782084510945, + "lanes": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "blocks_1h": 221, + "blocks_24h": 221, + "blocks_30m": 169, + "blocks_6h": 221, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781997600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 52, + "rejected": 0, + "rejected_local": 4, + "total": 56 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 67, + "rejected": 3, + "rejected_local": 1, + "total": 71 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 54, + "rejected": 0, + "rejected_local": 8, + "total": 62 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 48, + "rejected": 1, + "rejected_local": 4, + "total": 53 + }, + "start_unix_ms": 1782084000000 + } + ], + "client_addr": "192.168.1.101:37415", + "current_job_age_ms": 776, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084428171, + "last_event_unix_ms": 1782084489655, + "pdiff": 0.19984481443189672, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 4, + "rejected_local_30m": 13, + "remote_host": "192.168.1.101", + "totals": { + "accepted": 221, + "rejected": 4, + "rejected_local": 17, + "total": 242 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "blocks_1h": 257, + "blocks_24h": 257, + "blocks_30m": 200, + "blocks_6h": 257, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781997600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 57, + "rejected": 0, + "rejected_local": 4, + "total": 61 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 65, + "rejected": 1, + "rejected_local": 5, + "total": 71 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 65, + "rejected": 0, + "rejected_local": 8, + "total": 73 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 5, + "total": 75 + }, + "start_unix_ms": 1782084000000 + } + ], + "client_addr": "192.168.1.14:38993", + "current_job_age_ms": 776, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:3e:39:63", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084509807, + "last_event_unix_ms": 1782084509807, + "pdiff": 0.19879256220385763, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 1, + "rejected_local_30m": 18, + "remote_host": "192.168.1.14", + "totals": { + "accepted": 257, + "rejected": 1, + "rejected_local": 22, + "total": 280 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "blocks_1h": 231, + "blocks_24h": 231, + "blocks_30m": 183, + "blocks_6h": 231, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781997600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 48, + "rejected": 0, + "rejected_local": 5, + "total": 53 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 7, + "total": 77 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 63, + "rejected": 0, + "rejected_local": 3, + "total": 66 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 50, + "rejected": 0, + "rejected_local": 5, + "total": 55 + }, + "start_unix_ms": 1782084000000 + } + ], + "client_addr": "192.168.1.105:49283", + "current_job_age_ms": 776, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:4d:44:3a", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084505497, + "last_event_unix_ms": 1782084505497, + "pdiff": 0.1628804208937452, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 15, + "remote_host": "192.168.1.105", + "totals": { + "accepted": 231, + "rejected": 0, + "rejected_local": 20, + "total": 251 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "blocks_1h": 237, + "blocks_24h": 237, + "blocks_30m": 186, + "blocks_6h": 237, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781997600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 51, + "rejected": 0, + "rejected_local": 4, + "total": 55 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 62, + "rejected": 0, + "rejected_local": 4, + "total": 66 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 67, + "rejected": 0, + "rejected_local": 5, + "total": 72 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 57, + "rejected": 1, + "rejected_local": 4, + "total": 62 + }, + "start_unix_ms": 1782084000000 + } + ], + "client_addr": "192.168.1.102:46545", + "current_job_age_ms": 776, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084508154, + "last_event_unix_ms": 1782084508154, + "pdiff": 0.20427874252704384, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 1, + "rejected_local_30m": 13, + "remote_host": "192.168.1.102", + "totals": { + "accepted": 237, + "rejected": 1, + "rejected_local": 17, + "total": 255 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + } + ], + "last_update_age_seconds": 1, + "source": "pool-health-asic-performance", + "updated_at": "2026-06-21T23:28:30Z", + "updated_at_epoch": 1782084510, + "window_seconds": 86400 + }, + "can_accept_shares": true, + "can_submit_blocks": true, + "chain_block_count": "12207107", + "containers": { + "node": { + "running": true + }, + "pool": { + "running": true + } + }, + "miner_health": { + "configured_count": 4, + "connected_count": 4, + "connected_count_effective": 4, + "identity_contract": "mac-only", + "managed_count": 4, + "managed_miners": [ + { + "blocks_1h": "221", + "blocks_24h": "221", + "blocks_30m": "169", + "blocks_6h": "221", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782084428171", + "last_event_at_epoch_ms": "1782084489655", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.19984481443189672", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "4", + "rejected_local_30m": "13", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "237", + "blocks_24h": "237", + "blocks_30m": "186", + "blocks_6h": "237", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782084508154", + "last_event_at_epoch_ms": "1782084508154", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.20427874252704384", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "13", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "231", + "blocks_24h": "231", + "blocks_30m": "183", + "blocks_6h": "231", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782084505497", + "last_event_at_epoch_ms": "1782084505497", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.1628804208937452", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "15", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "257", + "blocks_24h": "257", + "blocks_30m": "200", + "blocks_6h": "257", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782084509807", + "last_event_at_epoch_ms": "1782084509807", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.19879256220385763", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "18", + "source": "env-overrides", + "status": "ok" + } + ], + "miners": [ + { + "blocks_1h": "221", + "blocks_24h": "221", + "blocks_30m": "169", + "blocks_6h": "221", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782084428171", + "last_event_at_epoch_ms": "1782084489655", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.19984481443189672", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "4", + "rejected_local_30m": "13", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "237", + "blocks_24h": "237", + "blocks_30m": "186", + "blocks_6h": "237", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782084508154", + "last_event_at_epoch_ms": "1782084508154", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.20427874252704384", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "13", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "231", + "blocks_24h": "231", + "blocks_30m": "183", + "blocks_6h": "231", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782084505497", + "last_event_at_epoch_ms": "1782084505497", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.1628804208937452", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "15", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "257", + "blocks_24h": "257", + "blocks_30m": "200", + "blocks_6h": "257", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "776", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782084509807", + "last_event_at_epoch_ms": "1782084509807", + "last_seen_at": "2026-06-21T23:28:15Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.19879256220385763", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "18", + "source": "env-overrides", + "status": "ok" + } + ], + "pool_ready_overlay": true, + "source": "redis-asic-status", + "source_key": "", + "tracked_count": 4 + }, + "mode": "mining", + "overall": "ok", + "pool_health": { + "accepted_block_count": "946", + "block_submit_error_count": "6", + "block_submit_success_count": "946", + "source_job_health": { + "authorized_miners": "4", + "ready_miners": "4" + }, + "stale_jobs": false, + "valid_share_count": "1972" + }, + "pool_metrics": { + "accepted_block_recent": true, + "accepted_block_submissions": 946, + "accepted_block_submissions_delta": 2, + "accepted_block_submissions_rate_per_second": 0.4, + "active_connections": 4, + "authorized_miners": 4, + "backend_main_order": 12207097, + "backend_mineable": 1, + "backend_p2p_mining_fresh": 1, + "backend_peer_lead_blocks": -1, + "backend_submit_ready": 1, + "blocks_found_total": 952, + "expired_miners": 0, + "invalidated_miners": 0, + "last_accepted_block_submission_age_seconds": 0, + "last_accepted_block_submission_at_epoch": 1782084510, + "ready_miners": 4, + "rejected_block_submissions": 6, + "shares_accepted_total": 1972, + "shares_rejected_total": 606, + "stale_miners": 0, + "template_age_seconds": 0.466 + }, + "source": "redis-live", + "status": "ok", + "sync_progress": { + "best_peer_mainorder": 12207106, + "chain_block_count": 12207107, + "chain_rpc_source": "getBlockCount", + "chain_syncing": false, + "current_block": 12207107, + "eth_syncing": { + "currentBlock": "0xb4a9cf", + "highestBlock": "0xb4efd5", + "startingBlock": "0x0" + }, + "evm_block_count": 11839951, + "evm_chain_syncing": true, + "evm_rpc_url": "http://host.docker.internal:18545", + "evm_sync_advisory": "eth_syncing active while native P2P mining state is current", + "highest_block": 12207107, + "mining_advisory_sync": true, + "native_is_current": true, + "p2p_connections": 18, + "p2p_network_gap": 0, + "p2p_network_height": 12207106, + "peer_count": 18, + "peer_mainorder_gap": 0, + "remaining_blocks": 0, + "source": "native-rpc", + "status": "synced", + "sync_current_block": 11839951, + "sync_highest_block": 11857877 + }, + "updated_at": "2026-06-21T23:28:31Z" + }, + "docker_ps": "NAMES STATUS IMAGE\npool Up 34 minutes stack-pool\nnode Up About an hour stack-node\nstatus-sampler Up 2 hours stack-status-sampler\ndashboard Up 2 hours (healthy) stack-dashboard\npostgres Up 3 hours (healthy) postgres:15-bookworm", + "docker_stats": "NAME CPU % MEM USAGE / LIMIT BLOCK I/O NET I/O\nnode 32.93% 1.985GiB / 15.31GiB 250MB / 119GB 0B / 0B\npool 3.79% 25.64MiB / 15.31GiB 8.19kB / 0B 0B / 0B\ndashboard 0.37% 31.77MiB / 15.31GiB 15.2MB / 445MB 167MB / 42.7MB\nstatus-sampler 5.07% 29.16MiB / 15.31GiB 32.9MB / 42MB 65MB / 9.89MB", + "env_file": "COMPOSE_PROJECT_NAME=stack\nDOCKERFILE=dockerfile-dev\nDOCKER_PLATFORM=linux/amd64\n# Optional overrides for dockerfile-dev named build contexts.\"\n# BLOCKDAG_CORECHAIN_CONTEXT=../blockdag-corechain\n# POOL_SRC_CONTEXT=../pool\n# CPU_MINER_SRC_CONTEXT=../cpu-miner\nCOLLECTOR_SRC_CONTEXT=/home/jeremy/blockdag-source/collector\n# DASHBOARD_SRC_CONTEXT=../dashboard2\"\n# Paths are relative to tarball root (same directory as docker-compose.yml).\n# To import a snapshot during build, place latest.bdsnap in the payload root and\n# set SNAPSHOT_PATH=./latest.bdsnap. The default marker means \"build without a\n# baked-in snapshot.\"\nSNAPSHOT_PATH=docker/no-snapshot.marker\n# SNAPSHOT_PATH=docker/latest.bdsnap\"\n\n# Snapshot download link for this node. The installer writes this based on the\n# selected install mode. Naming convention on the snapshot host:\n# latest.bdsnap -> non-archive (pruned) snapshot\n# latest-archive.bdsnap -> archive (full history) snapshot\n# The node entrypoint downloads from this URL at first start when no local\n# snapshot or chain data exists.\nBDAG_SNAPSHOT_URL=\n# Set to 1 to run the node in archival mode (--archival; consensus keeps all\n# blocks instead of pruning). Archive nodes should bootstrap from the archive\n# snapshot so historical data is actually present.\nBDAG_NODE_ARCHIVAL=0\n# Optional compose services:\n# blank -> base stack only\n# miner -> include CPU miner service (requires DOCKERFILE=dockerfile-dev)\n# COMPOSE_PROFILES=miner\n\n# Dashboard repository. Release builds always use the main branch.\nDASHBOARD_REPO=https://github.com/BlockdagEngineering/redis-dash.git\nP2P_PORT=8155\nBDAG_RPC_PORT=38131\nEVM_HTTP_PORT=18545\nEVM_WS_PORT=18546\nWALLET_RPC_URL=http://127.0.0.1:18545\nNODE_METRICS_PORT=6060\nPOOL_BIND_PORT=3334\nPOOL_API_PORT=9090\nCOLLECTOR_HOST_PORT=9280\nDASHBOARD_HOST_PORT=8088\n# Dashboard UI display timezone.\nDASHBOARD_TIMEZONE=Africa/Johannesburg\n# NODE_DATA_DIR=./node-data\n\n# Required for any pool/miner deployment. Do not use the zero address.\n# POOL_COINBASE_ADDRESS may be set separately when it must differ from the\n# displayed miner/pool identity; otherwise set MINING_POOL_ADDRESS only.\nMINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\n# Storage placement for constrained mining appliances. Release installers keep\n# growing node chain data on capacity storage, then move frequent small writes\n# such as Postgres and dashboard/runtime state to internal storage when it has\n# enough free space.\nBDAG_STORAGE_PROFILE=auto\nBDAG_CHAIN_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_NODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nBDAG_POSTGRES_DATA_DIR=./data/postgres\nBDAG_RUNTIME_DIR=./ops/runtime\nBDAG_STORAGE_MIN_CHAIN_FREE_GIB=50\nBDAG_STORAGE_MIN_RUNTIME_FREE_GIB=4\n\n# Ephemeral scratch policy. Small temporary files belong on RAM-backed tmpfs so\n# they do not add avoidable writes to the USB chain device. Large snapshot and\n# chain staging must stay on capacity storage unless explicitly sized.\nBDAG_EPHEMERAL_TMPFS_ENABLED=1\nBDAG_EPHEMERAL_DIR=/run/bdag-pool\nBDAG_HOST_TMPDIR=/run/bdag-pool/tmp\nBDAG_CONTAINER_TMPFS_SIZE=128m\nBDAG_NODE_TMPFS_SIZE=512m\nBDAG_NODE_SHM_SIZE=512m\nBDAG_BUILD_TMPDIR=./.build-tmp\nBDAG_BUILD_CACHE_WARN_GIB=4\nBDAG_ROOT_FREE_WARN_GIB=6\nBDAG_ROOT_FREE_FAIL_GIB=2\n\n# Single P2P host port used by Docker publishing, firewall, and local peer\n# advertisement.\nBDAG_P2P_PROTOCOLS=tcp\n# Standard mining-host performance defaults. The release topology has one\n# production node with a direct pool-to-node RPC path.\nBDAG_NODE_CACHE_MB=6144\nBDAG_EVM_CACHE_MB=6144\nBDAG_NODE_CACHE_DATABASE_PERCENT=70\nBDAG_NODE_OBSOLETE_HEIGHT=20\nBDAG_NODE_MINING_NO_PENDING_TX=0\nBDAG_BLOCK_READ_AHEAD_KB=1024\nBDAG_BLOCK_NR_REQUESTS=256\nBDAG_NODE_CPU_SHARES=6144\nBDAG_POOL_CPU_SHARES=5120\nBDAG_POOL_DB_CPU_SHARES=4096\nBDAG_DASHBOARD_CPU_SHARES=128\nBDAG_NODE_MEMORY_LOW=768M\nBDAG_POOL_MEMORY_LOW=256M\nBDAG_POOL_DB_MEMORY_LOW=512M\nBDAG_DASHBOARD_MEMORY_LOW=64M\nBDAG_MINING_ACTIVE_NODE_NICE=-10\nBDAG_MINING_POOL_NICE=-8\nBDAG_OBSERVABILITY_NICE=15\nBDAG_TUNE_NET_QDISC=1\nBDAG_STACK_NET_IFACE=\nPOOL_RPC_BACKENDS=\n\n# Release install support services. Fresh appliance installs apply the\n# non-destructive host profile by default, then install P2P/IPFS support timers\n# and the mining-host tuning timer. Set the strict toggles to fail the install\n# instead of warning when host-level service installation cannot complete.\nBDAG_INSTALL_APPLIANCE_HOST_PROFILE=1\nBDAG_INSTALL_APPLIANCE_PROFILE_DISABLE_SERVICES=0\nBDAG_INSTALL_APPLIANCE_PROFILE_RELOAD_DOCKER=1\nBDAG_INSTALL_APPLIANCE_PROFILE_STRICT=0\nBDAG_INSTALL_STACK_SUPPORT_SERVICES=1\nBDAG_INSTALL_STACK_SUPPORT_SERVICES_STRICT=0\n\n# Enables localhost/private-network pool runtime admin endpoints. Keep the pool\n# metrics/admin port bound to localhost on public hosts.\nPOOL_RUNTIME_ADMIN_ENABLED=false\nMETRICS_ADDR=0.0.0.0:9090\nPOOL_GBT_MIN_INTERVAL_MS=1100\nPOOL_GBT_PRESSURE_INTERVAL_MS=500\nPOOL_GBT_PRESSURE_WINDOW_SECONDS=10\nPOOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15\nPOOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30\n# Host network discovery is only for choosing this node's advertised P2P address.\n# Peer candidates should be complete P2P multiaddrs.\nBDAG_NETWORK_TOPOLOGY=auto\nBDAG_DETECTED_NETWORK_TOPOLOGY=\nBDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\nBDAG_ASIC_LAN_CIDRS=192.168.1.0/24\n# Optional route to miners on a remote LAN reached through a routed gateway\n# (e.g. a ZeroTier peer). When set, the miner-route compose service keeps the\n# host route alive so the watchdog can reach the miner HTTP API. Leave empty\n# when miners are on the host LAN.\nBDAG_MINER_ROUTE_SUBNET=\nBDAG_MINER_ROUTE_GATEWAY=\nBDAG_MINER_ROUTE_DEV=\n# Docker bridge/container networks must not be treated as ASIC LANs. Override\"\n# only if a real ASIC LAN intentionally uses these ranges.\nBDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12\nBDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0\nBDAG_P2P_ADVERTISE_IP=\nBDAG_P2P_INTERFACE=\nBDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1\nBDAG_CHAIN_PEERSTORE_LOG_TAIL=8000\nEXTRA_PEER_ADDRESSES=\n# When enabled by installer/watchdog for attached ASICs, this must include\n# --miner, --miningaddr=, and --maxinbound=1 on constrained\n# USB/router hosts. Do not add --allowminingwhennearlysynced or\n# --allowsubmitwhennotsynced; template/readiness health must fail closed when\n# P2P freshness or sync safety is unavailable.\nBDAG_NODE_MINING_ARGS=\nBDAG_NODE_CHAIN_RPC_TIMEOUT=8.0\nBDAG_NODE_CHAIN_RPC_RETRIES=2\nBDAG_POOL_RPC_REFUSED_WARN_SECONDS=120\nBDAG_HOST_PRESSURE_IOWAIT_WARN_PERCENT=25\nBDAG_HOST_PRESSURE_IOWAIT_WARN_SAMPLES=3\nBDAG_HOST_PRESSURE_HISTORY_SAMPLES=6\nBDAG_SHARED_STATUS_CACHE_ENABLED=1\nBDAG_SHARED_STATUS_CACHE_SECONDS=3.0\nBDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS=120\nBDAG_HOST_PROFILE=auto\nBDAG_ADAPTIVE_CONCURRENCY_ENABLED=1\nBDAG_ADAPTIVE_IOWAIT_WARN_PERCENT=25\nBDAG_ADAPTIVE_IO_SOME_AVG10_WARN=20.0\nBDAG_ADAPTIVE_CPU_SOME_AVG10_WARN=80.0\nBDAG_ADAPTIVE_CHAIN_RPC_WARN_MS=1000\nBDAG_STATUS_SAMPLER_ENABLED=1\nBDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10\nBDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120\nBDAG_COLLECTOR_DIRECT_STATUS_FALLBACK=0\nBDAG_COLLECTOR_STATUS_CACHE_SECONDS=10\nBDAG_COLLECTOR_SAMPLER_CACHE_SECONDS=120\nBDAG_COLLECTOR_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_CHAIN_STATE_SELF_HEAL_ENABLED=1\nBDAG_CHAIN_STATE_SELF_HEAL_UNIT=bdag-chain-state-self-heal.service\nBDAG_MINING_IMPERATIVE_CHAIN_STATE_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_SECONDS=900\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS=1000\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_GAP_GROWTH_BLOCKS=60\nBDAG_CHAIN_STATE_REUSE_EXISTING_SNAPSHOT=1\n# Optional trusted restore source for unattended chain-state self-heal.\"\n# Use key-based SSH for remote rsync sources; do not store passwords here.\nBDAG_CHAIN_STATE_RESTORE_SOURCE=\nBDAG_CHAIN_STATE_RESTORE_SNAPSHOT=\nBDAG_CHAIN_STATE_RESTORE_SSH_COMMAND=\nBDAG_DASHBOARD_DIRECT_STATUS_FALLBACK=0\nBDAG_DASHBOARD_STATUS_CACHE_SECONDS=10\nBDAG_DASHBOARD_SAMPLER_CACHE_SECONDS=120\nBDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_EARNINGS_SAMPLER_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_GLOBAL_SAMPLER_INTERVAL_SECONDS=60\nBDAG_OPS_UID=1000\nBDAG_OPS_GID=1000\nBDAG_DOCKER_SOCKET_GID=115\nBDAG_GLOBAL_CACHE_TTL_SECONDS=60\nBDAG_GLOBAL_RPC_WORKERS=4\nBDAG_GLOBAL_BLOCK_WINDOW=600\nBDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOTS=1\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_HOURS=720\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WINDOW_BLOCKS=64\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WORKERS=12\nBDAG_DASHBOARD_HISTORY_REBUILD_PRESERVE_ASIC_HISTORY=1\n# Optional native mining RPC list for Global chain-production stats.\"\n# EVM RPC variables are wallet/balance sources only and must not drive Global mining attribution.\nBDAG_GLOBAL_CHAIN_RPC_URLS=\nBDAG_MINER_SCAN_WORKERS=64\nBDAG_MINER_HASHRATE_PROBE_WORKERS=8\nBDAG_BACKGROUND_MAINTENANCE_BACKOFF_ENABLED=1\nBDAG_BACKGROUND_MAINTENANCE_SYNC_BACKOFF_BLOCKS=0\nBDAG_BACKGROUND_MAINTENANCE_IOWAIT_WARN_PERCENT=25\nBDAG_BACKGROUND_MAINTENANCE_IO_SOME_AVG10_WARN=20.0\nBDAG_BACKGROUND_MAINTENANCE_CPU_SOME_AVG10_WARN=80.0\nBDAG_BACKGROUND_MAINTENANCE_CHAIN_RPC_WARN_MS=1000\nBDAG_BACKGROUND_MAINTENANCE_POOL_READY_TASKS=rawdatadir_publish,ipfs_content_sidecar,ipfs_segment_writer\nBDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=2\nBDAG_ENTRYPOINT_CHOWN_MODE=needed\nBDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\n# Current Compose/service-name topology. Source Compose intentionally does not\n# pin container_name; Docker Compose may append project and ordinal suffixes to\n# concrete container names.\nBDAG_POOL_CONTAINER=pool\nBDAG_POOL_CONTAINERS=pool\nBDAG_POOL_DB_CONTAINER=postgres\nBDAG_NODE_SERVICES=node\nBDAG_STACK_SERVICES=postgres,node,pool\nBDAG_NODE_RPC_URLS=node=http://127.0.0.1:38131\n\n# Optional pre-start P2P snapshot bootstrap. When the node datadir is empty,\n# the node image uses ordinary trusted/addpeer candidates for FastSnap V2 first,\n# imports the verified snapshot, then normal FastSync catches the remaining tail.\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=90s\nBDAG_FASTSNAP_ARTIFACT_V2=1\n# Prefer Fast Artifact Sync V2 directory artifacts when a peer offers them.\n# This avoids building/importing a temporary .bdsnap archive. The archive\n# snapshot RPC path is used only when the serving peer lacks directory support.\nBDAG_FASTSNAP_DIRECTORY_MODE=1\nBDAG_FASTSNAP_DIRECTORY_STAGING=\nBDAG_FASTSNAP_DIRECTORY_REPLACE_EXISTING=1\nBDAG_FASTSNAP_DIRECTORY_MOVE_STAGING=1\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_PARALLELISM=4\nBDAG_FASTSNAP_LEDGER=\n# Archive FastSnap seed serving is deprecated for release installs.\n# The raw datadir FastArtifact source sidecar below is the preferred sync-source\n# path. Keep this disabled unless an offline export workflow is being\n# investigated away from the live mining host.\nBDAG_FASTSNAP_SEED_TIMER_ENABLED=0\n# Raw datadir FastArtifact source sidecar. Installer auto-enables the timer only\n# when chain/stage/artifact paths are not USB/removable and the host has enough\n# CPU, RAM, and disk headroom. Publishes require an explicit operator-approved\n# finalization window.\nSYNC_SOURCE_NODE=0\nBDAG_RAWDATADIR_ARTIFACT_BASE=./data-restore/rawdatadir\nBDAG_RAWDATADIR_ARTIFACT_KEEP=3\nBDAG_RAWDATADIR_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_REQUIRE_EVM_REFERENCE_FRESH=1\nBDAG_RAWDATADIR_MAX_EVM_REFERENCE_LAG=1000\nBDAG_PUBLIC_RPC_URLS=bdagscan-rpc=https://rpc.bdagscan.com,blockdag-engineering-rpc=https://rpc.blockdag.engineering\nBDAG_RAWDATADIR_SIDECAR_SOURCE=\nBDAG_RAWDATADIR_SIDECAR_DIR=\nBDAG_RAWDATADIR_SIDECAR_CONTENT_MODE=auto\nBDAG_RAWDATADIR_SIDECAR_CONTENT_BASE=./data-restore/rawdatadir-sidecar-content\nBDAG_RAWDATADIR_SIDECAR_CONTENT_KEEP=2\nBDAG_RAWDATADIR_SIDECAR_CONTENT_CHUNK_SIZE=67108864\nBDAG_RAWDATADIR_SIDECAR_CONTENT_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_SIDECAR_CONTENT_ALLOW_HOT_PUBLISH=0\nBDAG_RAWDATADIR_OPEN_SIDECAR_ENABLED=1\nBDAG_RAWDATADIR_OPEN_SIDECAR_BASE=./data-restore/rawdatadir-sidecar-open/mainnet\nBDAG_RAWDATADIR_OPEN_SIDECAR_KEEP=12\nBDAG_RAWDATADIR_SIDECAR_RSYNC_BWLIMIT=4096\nBDAG_RAWDATADIR_SIDECAR_DELAY_UPDATES=0\nBDAG_RAWDATADIR_ACTIVE_SERVICE=node\nBDAG_RAWDATADIR_FINALIZE=0\nBDAG_RAWDATADIR_MIN_FREE_GIB=100\nBDAG_RAWDATADIR_FREE_SPACE_MULTIPLIER=2.5\nBDAG_RAWDATADIR_REQUIRE_STATE_ROOT=1\nBDAG_RAWDATADIR_SIDECAR_USE_SUDO=auto\nBDAG_RAWDATADIR_ARCHIVE_USE_SUDO=auto\nBDAG_RAWDATADIR_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_RAWDATADIR_TRUSTED_SIGNERS=\n# Optional Fast Artifact Sync V2 directory hot-stage serving. Set these only on\n# nodes that maintain a verified directory artifact mirror. The manifest path is\n# allowed to be a sidecar outside the directory to avoid copying control files\n# into a future install target.\nBDAG_FASTSYNC_ARTIFACT_DIRECTORY=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST=\n\n# Lazy IPFS content sidecar. The rawdatadir sidecar seals immutable signed\n# file/chunk generations into BDAG_RAWDATADIR_SIDECAR_CONTENT_BASE; IPFS pins\n# only publishable generations after mining and host resource gates pass. IPFS\n# is byte transport; signed manifests and consensus validation remain\n# authoritative.\nBDAG_IPFS_CONTENT_SIDECAR_MODE=auto\nBDAG_IPFS_CONTENT_ARTIFACT_DIR=./data-restore/rawdatadir-sidecar-content/current\nBDAG_IPFS_CONTENT_ARTIFACT_MANIFEST=./data-restore/rawdatadir-sidecar-content/current/manifest.json\nBDAG_IPFS_CONTENT_ALLOW_UNSIGNED_ARTIFACT=0\nBDAG_IPFS_CONTENT_PUBLISH_IPNS=0\nBDAG_IPFS_CONTENT_IPNS_KEY=\nBDAG_IPFS_CONTENT_REPUBLISH_IPNS_WHILE_WAITING=1\nBDAG_IPFS_CONTENT_IPNS_TTL=1m\nBDAG_IPFS_CONTENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_CONTENT_ADD_ARGS=\"--recursive --cid-version=1 --raw-leaves --pin=true --quieter\"\nBDAG_IPFS_CONTENT_DISCOVERY_FILE=./ops/ipfs-content-discovery.json\nBDAG_IPFS_CONTENT_LATEST_IPNS=/ipns/k51qzi5uqu5djjlh4vxtmzyswx0qk4s3wdlf3yrpkszp38gq5sl71zcgmmc3jk\nBDAG_IPFS_CONTENT_DEFAULT_INDEX_CID=bafkreia7jk2ljqi3raiohugp6nw3633njfp7jmnuvqh47po52et4kupu2a\nBDAG_IPFS_CONTENT_DEFAULT_ROOT_CID=\nBDAG_IPFS_CONTENT_STATUS_FILE=./ops/runtime/ipfs-content-sidecar-status.json\nBDAG_IPFS_CONTENT_LATEST_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\nBDAG_IPFS_SEGMENT_WRITER_MODE=auto\nBDAG_IPFS_SEGMENT_START_POLICY=live_tail\nBDAG_IPFS_SEGMENT_FINALITY_LAG_ORDERS=600\nBDAG_IPFS_SEGMENT_ORDERS_PER_SEGMENT=300\nBDAG_IPFS_SEGMENT_MAX_SEGMENTS_PER_RUN=1\nBDAG_IPFS_SEGMENT_MAX_RPC_PER_SECOND=25\nBDAG_IPFS_SEGMENT_RPC_TIMEOUT=8\nBDAG_IPFS_SEGMENT_BLOCK_RPC_RETRIES=2\nBDAG_IPFS_SEGMENT_PUBLISH_IPNS=0\nBDAG_IPFS_SEGMENT_IPNS_KEY=\nBDAG_IPFS_SEGMENT_IPNS_TTL=1m\nBDAG_IPFS_SEGMENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_SEGMENT_STATUS_FILE=./ops/runtime/ipfs-content/segment-writer-status.json\nBDAG_IPFS_SEGMENT_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\n\n# FastSync candidates for new/empty nodes and startup peers. Entries must be\n# complete P2P multiaddrs with peer IDs. Address class is not a sync mode or\n# priority signal; libp2p/FastArtifact probing should pick useful peers by\n# measured P2P latency, manifest/chunk response, and sustained transfer.\nBDAG_FASTSYNC_PEER_ORDERING=p2p-latency\nBDAG_FASTSYNC_APPEND_ADDPEERS=1\nBDAG_FASTARTIFACTSYNC_ENABLED=1\nBOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_RANGE_BLOCKS=1024\nBDAG_FASTSYNC_PREPROCESS_WORKERS=1\nBDAG_SYNC_COORDINATOR_ACCELERATE_FASTSYNC=1\nBDAG_SYNC_COORDINATOR_FAST_RESTART_COOLDOWN_SECONDS=900\nBDAG_SYNC_COORDINATOR_RESTART_ON_STALE_IMPORT=1\nBDAG_CATCHUP_PAUSE_ENABLED=1\nBDAG_CATCHUP_PAUSE_ON_SYNCING=1\nBDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300\nBDAG_CATCHUP_IO_PRESSURE_PAUSE_ENABLED=1\nBDAG_CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS=25\nBDAG_CATCHUP_IOWAIT_WARN_PERCENT=15\nBDAG_CATCHUP_IO_SOME_AVG10_WARN=20.0\nBDAG_CATCHUP_IO_FULL_AVG10_WARN=10.0\nBDAG_CATCHUP_NODE_RECREATE_ENABLED=1\nBDAG_CATCHUP_NODE_CACHE_MB=6144\nBDAG_CATCHUP_NODE_CACHE_MIN_MB=1024\nBDAG_CATCHUP_NODE_CACHE_MEMORY_PERCENT=40\nBDAG_FAST_CATCHUP_ARTIFACT_MODE=auto\nBDAG_FAST_CATCHUP_ARTIFACT_RETRY_SECONDS=300\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_BEHIND_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_GAIN_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_TRUST_ON_FIRST_SIGNED=1\nBDAG_FAST_CATCHUP_ALLOW_UNSIGNED_ARTIFACTS=0\nBDAG_FAST_CATCHUP_ARTIFACT_TIMEOUT=21600s\nNODE_RPC_USER=test\nNODE_RPC_PASS=\nNODE_RPC_URLS=http://127.0.0.1:38131\n# Optional block-candidate submit override. Leave blank to use NODE_RPC_URLS.\"\n# Named entries are allowed: local=http://node:38131\nPOOL_SUBMIT_RPC_URLS=\n# Extra node flags.\nNODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\nBDAG_FASTSYNC_SNAPSHOT_PATH=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_ID=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_HEX=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST_TTL=24h\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS_FILE=/etc/bdagStack/node.conf\nBDAG_FASTSNAP_AUTO_MAX_PEERS=3\nBDAG_FASTSNAP_LEGACY_FALLBACK=0\n# Production pool deployments are mainnet-only. Install/runtime scripts reject\n# any rawdatadir or FastSnap network value other than mainnet.\nBDAG_FASTSNAP_NETWORK=mainnet\nBDAG_RAWDATADIR_NETWORK=mainnet\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=300s\nBDAG_FASTSNAP_PARALLELISM=8\nBDAG_FASTSNAP_RETRIES=5\nBDAG_FASTSNAP_RETRY_DELAY=10s\nBDAG_FASTSNAP_TRUSTED_SIGNERS=\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_KEEP_ARCHIVE=0\nPOSTGRES_USER=bdag_pool\nPOSTGRES_DB=bdagpool\nPOSTGRES_PASSWORD=\n# ---------------------------------------------------------------------------\n# Mining pool \u2014 passed into the `pool` container environment.\n# ---------------------------------------------------------------------------\nPOOL_BIND_ADDR=192.168.1.100:3334\nPPLNS_N_WORK=1000\nPOOL_FEE_PERCENTAGE=1.0\nPOOL_BLOCK_MATURITY=10\nPOOL_PAYOUT_MATURITY=4096\n# CPU miner (only used when COMPOSE_PROFILES includes \"miner\").\nMINER_POOL_URL=stratum+tcp://192.168.1.100:3334\nMINER_POOL_PASS=\nMINER_WORKERS=1\n\n\n\n# ASIC block-candidate timing pinned to the best observed 2026-06-21\n# 05:40-05:57 SAST production window.\nPOOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true\nPOOL_TEMPLATE_TTL_REFRESH_MS=100\nPOOL_SUBMIT_STALE_BLOCK_CANDIDATES=false\nPOOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750\nPOOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100\nPOOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true\nPOOL_STALE_RACE_REJECT_WINDOW_SECONDS=10\nPOOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1\nPOOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0\nPOOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60\nPOOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true\nPOOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true\nSTACK_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nBLOCKDAG_CORECHAIN_CONTEXT=/home/jeremy/blockdag-source/blockdag-corechain\nPOOL_SRC_CONTEXT=/home/jeremy/blockdag-source/pool\nCPU_MINER_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nDASHBOARD_SRC_CONTEXT=/home/jeremy/blockdag-source/redis-dash\nCOMPOSE_PROFILES=\nNODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nSNAPSHOT_HOST_PATH=./docker/no-snapshot.marker\nBDAG_REQUIRE_SNAPSHOT=0\nBDAG_ENABLE_NODE_MINING=1\nBDAG_NODE_MODULES=Blockdag,miner\nBDAG_POOL_HOST=192.168.1.100\nBDAG_POOL_URL=stratum+tcp://192.168.1.100:3334\nBDAG_MINER_SCAN_TARGET=192.168.1.0/24\nBDAG_MINER_CONTROL_ENABLED=0\nDASHBOARD_BIND=127.0.0.1\nNODE_RPC_URL=http://127.0.0.1:38131\nPOOL_DB_HOST=127.0.0.1\nPOOL_DB_PORT=55432\nMINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_PRIVATE_KEY=\nPOOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\nPOOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\nPOOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp\nPOOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30\nPOOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com\nBDAG_POOL_START_GATE_MIN_PEERS=2\nBDAG_NODE_DEBUG_LEVEL=warn\nBDAG_NODE_NO_FILE_LOGGING=1\n", + "hard_safety_rule": "Live node must remain --modules=Blockdag --modules=miner --miner --miningaddr=; no p2p module in live/default/test/generated paths.", + "node_command_env": "[\"/usr/local/bin/docker-entrypoint-nodeworker.sh\",\"/usr/local/bin/nodeworker\",\"--node-binary=/usr/local/bin/blockdag-node\",\"--node-args=--configfile /etc/bdagStack/node.conf\",\"--rpc-url=ws://127.0.0.1:18546\",\"--dag-rpc-url=http://127.0.0.1:38131\",\"--persist-root=/var/lib/bdagStack/nodeworker\",\"--health-min-peers=2\",\"--rollout-window=30m\"] null [\"BDAG_ENTRYPOINT_CHOWN_MODE=needed\",\"BDAG_NODE_MINING_NO_PENDING_TX=0\",\"TEMP=/tmp\",\"BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral\",\"MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"TMP=/tmp\",\"BDAG_NODE_MODULES=Blockdag,miner\",\"MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\",\"BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\",\"SYNC_SOURCE_NODE=0\",\"BDAG_NODE_ARCHIVAL=0\",\"BDAG_SNAPSHOT_URL=\",\"P2P_PORT=8155\",\"BDAG_STORAGE_PROFILE=auto\",\"BDAG_DETECTED_NETWORK_TOPOLOGY=\",\"BDAG_ASIC_LAN_CIDRS=192.168.1.0/24\",\"BDAG_ENABLE_NODE_MINING=1\",\"BDAG_NODE_NO_FILE_LOGGING=1\",\"BDAG_NETWORK_TOPOLOGY=auto\",\"BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\",\"BDAG_P2P_ADVERTISE_IP=\",\"BDAG_P2P_INTERFACE=\",\"BDAG_NODE_MINING_ARGS=\",\"TMPDIR=/tmp\",\"BDAG_NODE_OBSOLETE_HEIGHT=20\",\"POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODE_DEBUG_LEVEL=debug\",\"BDAG_NETWORK=\",\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"]", + "node_conf": "# BlockDAG node configuration for the Docker Compose stack.\n\n# P2P listen\nlisten=0.0.0.0\nport=8150\n\n# BDAG RPC listen\nrpclisten=0.0.0.0:38131\n\n# EVM RPC listen\nevm.http.addr=0.0.0.0\nevm.http.port=18545\nevm.ws.addr=0.0.0.0\nevm.ws.port=18546\n\n\n# Bootstrap peers (mainnet seed nodes). Add one addpeer=... line per complete\n# BDAG P2P multiaddr. Keep only public peers that are currently reachable.\n\n\n\n\naddpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo\naddpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\naddpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz\naddpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD\naddpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU\naddpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm\naddpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv\naddpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn\naddpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta\naddpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ\naddpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME\naddpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus\naddpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG\naddpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ\naddpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY\naddpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G\naddpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7\naddpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1\naddpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP\naddpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X\naddpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB\naddpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9\n\n# Peer limit\nmaxpeers=350\ncache=6144\ncache.database=70\n\n# RPC auth. Must match NODE_RPC_USER / NODE_RPC_PASS in .env.\nrpcuser=test\nrpcpass=\n\n# Storage paths\ndatadir=/var/lib/bdagStack/node\nlogdir=/var/log/bdagStack\n\n# Native metrics endpoint\nmetrics=true\nprofile=0.0.0.0:6060\n\n# Keep empty unless node mining is intentionally enabled.\nminingaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\nmodules=Blockdag,miner\n\nevm.http.api=eth,net,web3,txpool,debug\nhttp.readtimeout=45s\nhttp.writetimeout=45s\nhttp.idletimeout=45s\nevmenv=\"--rpc.allow-unprotected-txs --metrics --metrics.addr 0.0.0.0 --metrics.port 6060 --cache 6144\"\nrpcmaxclients=64\nrpcmaxclientsperhost=16\nrpcmaxconcurrentreqs=500\nrpcrequesttimeout=60s\nhttp.ratelimit=1800\nhttp.rateburst=3200\n", + "p2p_guard": { + "code": 1, + "ok": false, + "out": "" + }, + "pool_metrics_tail": "pool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"5\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"10\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"25\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"50\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"100\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"250\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"1000\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"+Inf\"} 103\npool_submit_job_sequence_distance_sum{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\"} 106\npool_submit_job_sequence_distance_count{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\"} 103\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 4\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 4\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 6\npool_submit_job_sequence_distance_sum{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\"} 10\npool_submit_job_sequence_distance_count{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\"} 6\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 5\npool_submit_job_sequence_distance_sum{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 5\npool_submit_job_sequence_distance_count{classification=\"retained_same_parent_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 5\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 44\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 49\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 100\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 49\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1\"} 0\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"2\"} 8\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"3\"} 26\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"5\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"10\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"25\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"50\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"100\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"250\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1000\"} 30\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"+Inf\"} 30\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 86\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 30\n# HELP pool_submit_read_to_handle_latency_seconds Time between reading a mining.submit request from the Stratum socket and starting submit handling.\n# TYPE pool_submit_read_to_handle_latency_seconds histogram\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0001\"} 3474\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0005\"} 3554\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.001\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"1\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"2\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"5\"} 3555\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 3555\npool_submit_read_to_handle_latency_seconds_sum{pool_id=\"0\"} 0.16640566899999962\npool_submit_read_to_handle_latency_seconds_count{pool_id=\"0\"} 3555\n# HELP pool_submit_stall_window_failures Submit-stall recovery failures observed in the current window by kind.\n# TYPE pool_submit_stall_window_failures gauge\npool_submit_stall_window_failures{kind=\"all\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"duplicate\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"stale\",pool_id=\"0\"} 0\n# HELP pool_template_broadcasts_total Total block templates broadcast to connected miners.\n# TYPE pool_template_broadcasts_total counter\npool_template_broadcasts_total{pool_id=\"0\"} 2837\n# HELP pool_template_conversion_stall_active_miners Active authorized miners observed by the template-conversion stall detector.\n# TYPE pool_template_conversion_stall_active_miners gauge\npool_template_conversion_stall_active_miners{pool_id=\"0\"} 4\n# HELP pool_template_conversion_stall_failure_ratio Failed candidate block percentage in the current template-conversion stall window.\n# TYPE pool_template_conversion_stall_failure_ratio gauge\npool_template_conversion_stall_failure_ratio{pool_id=\"0\"} 20.58823529411765\n# HELP pool_template_conversion_stall_window_candidates Candidate block outcomes observed in the current template-conversion stall window by kind.\n# TYPE pool_template_conversion_stall_window_candidates gauge\npool_template_conversion_stall_window_candidates{kind=\"accepted\",pool_id=\"0\"} 27\npool_template_conversion_stall_window_candidates{kind=\"duplicate\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"failed\",pool_id=\"0\"} 7\npool_template_conversion_stall_window_candidates{kind=\"stale\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"total\",pool_id=\"0\"} 34\n# HELP pool_template_fetch_duration_seconds Time to fetch a block template from the node via RPC.\n# TYPE pool_template_fetch_duration_seconds histogram\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 1190\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 1466\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 2132\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 2368\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 2450\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 2531\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 2636\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"1\"} 2720\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"2\"} 2756\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"5\"} 2766\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 2766\npool_template_fetch_duration_seconds_sum{pool_id=\"0\"} 213.9270945039997\npool_template_fetch_duration_seconds_count{pool_id=\"0\"} 2766\n# HELP pool_vardiff_adjustments_total VarDiff difficulty adjustments by direction.\n# TYPE pool_vardiff_adjustments_total counter\npool_vardiff_adjustments_total{direction=\"anti-spiral-hold\",pool_id=\"0\"} 5\npool_vardiff_adjustments_total{direction=\"decrease\",pool_id=\"0\"} 4\npool_vardiff_adjustments_total{direction=\"hold\",pool_id=\"0\"} 69\npool_vardiff_adjustments_total{direction=\"increase\",pool_id=\"0\"} 69\n# HELP pool_vardiff_floor_hits_total VarDiff retarget attempts clamped by the configured minimum pool difficulty.\n# TYPE pool_vardiff_floor_hits_total counter\npool_vardiff_floor_hits_total{pool_id=\"0\"} 0\n# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.\n# TYPE process_cpu_seconds_total counter\nprocess_cpu_seconds_total 25.94\n# HELP process_max_fds Maximum number of open file descriptors.\n# TYPE process_max_fds gauge\nprocess_max_fds 524287\n# HELP process_network_receive_bytes_total Number of bytes received by the process over the network.\n# TYPE process_network_receive_bytes_total counter\nprocess_network_receive_bytes_total 5.3590214918e+10\n# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network.\n# TYPE process_network_transmit_bytes_total counter\nprocess_network_transmit_bytes_total 3.9952284864e+10\n# HELP process_open_fds Number of open file descriptors.\n# TYPE process_open_fds gauge\nprocess_open_fds 18\n# HELP process_resident_memory_bytes Resident memory size in bytes.\n# TYPE process_resident_memory_bytes gauge\nprocess_resident_memory_bytes 4.1889792e+07\n# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.\n# TYPE process_start_time_seconds gauge\nprocess_start_time_seconds 1.7820824185e+09\n# HELP process_virtual_memory_bytes Virtual memory size in bytes.\n# TYPE process_virtual_memory_bytes gauge\nprocess_virtual_memory_bytes 2.599043072e+09\n# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.\n# TYPE process_virtual_memory_max_bytes gauge\nprocess_virtual_memory_max_bytes 1.8446744073709552e+19\n# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.\n# TYPE promhttp_metric_handler_requests_in_flight gauge\npromhttp_metric_handler_requests_in_flight 1\n# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.\n# TYPE promhttp_metric_handler_requests_total counter\npromhttp_metric_handler_requests_total{code=\"200\"} 647\npromhttp_metric_handler_requests_total{code=\"500\"} 0\npromhttp_metric_handler_requests_total{code=\"503\"} 0", + "predeploy_notes": [ + "Source tests passed for pool and redis-dash before this savepoint.", + "Preview screenshots were captured on port 18088 without touching live containers.", + "Current live dashboard evidence reports Mining, can_accept_shares=true, can_submit_blocks=true, and four ASIC rows ready in the recent sample." + ], + "purpose": "Retained savepoint before deploying redis-dash sidebar presentation transplant and pool node-syncing/P2P-safe job-preservation patch.", + "rendered_compose_config": "name: stack\nservices:\n dashboard:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: dashboard\n cpu_shares: 256\n container_name: dashboard\n depends_on:\n node:\n condition: service_started\n required: true\n pool:\n condition: service_started\n required: true\n environment:\n ADDR: 0.0.0.0:8088\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS: \"3\"\n BDAG_CHAIN_POLL_SECONDS: \"2\"\n BDAG_CHAIN_WS_READ_LIMIT_BYTES: \"1048576\"\n BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS: \"10\"\n BDAG_DASHBOARD_HEALTH_STALE_SECONDS: \"20\"\n BDAG_DASHBOARD_REDIS_ENABLED: \"1\"\n BDAG_DASHBOARD_REDIS_MANAGED: \"1\"\n BDAG_EVM_HTTP_URL: http://host.docker.internal:18545\n BDAG_EVM_SYNC_BACKOFF_SECONDS: \"60\"\n BDAG_EVM_WS_URL: ws://host.docker.internal:18546\n BDAG_GLOBAL_BLOCK_WINDOW: \"600\"\n BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS: \"30\"\n BDAG_GLOBAL_FAST_WARMUP_BLOCKS: \"120\"\n BDAG_GLOBAL_MAX_INCREMENTAL_GAP: \"120\"\n BDAG_GLOBAL_RPC_CHUNK_SIZE: \"64\"\n BDAG_GLOBAL_RPC_WORKERS: \"4\"\n BDAG_GLOBAL_SETTLE_DELAY_SECONDS: \"5\"\n BDAG_MINER_CONTROL_ENABLED: \"0\"\n BDAG_NODE_RPC_URL: http://host.docker.internal:38131\n BDAG_POOL_METRICS_URL: http://host.docker.internal:9090/metrics\n BDAG_POOL_URL: stratum+tcp://192.168.1.100:3334\n BDAG_REDIS_APPENDFSYNC: everysec\n BDAG_REDIS_APPENDONLY: \"yes\"\n BDAG_REDIS_CONFIG: /etc/redis/redis.conf\n BDAG_REDIS_MAXMEMORY: 256mb\n BDAG_REDIS_MAXMEMORY_POLICY: noeviction\n BDAG_REDIS_SOCKET: /run/dashboard-redis/redis.sock\n BDAG_TREND_BACKFILL_CHUNK_SIZE: \"128\"\n BDAG_TREND_BACKFILL_DAYS: \"30\"\n BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS: \"4096\"\n BDAG_TREND_BACKFILL_SLEEP_MS: \"100\"\n BDAG_TREND_CATCHUP_INTERVAL_SECONDS: \"30\"\n BDAG_TREND_HISTORY_ENABLED: \"1\"\n BDAG_TREND_RECENT_WARMUP_BLOCKS: \"3600\"\n BDAG_TREND_RPC_WORKERS: \"4\"\n BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS: \"2048\"\n BDAG_TREND_SPARSE_SAMPLES_PER_PASS: \"24\"\n DASHBOARD_TIMEZONE: Africa/Johannesburg\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n POOL_ASIC_ARP_TABLE_PATH: /host/proc/net/arp\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n healthcheck:\n test:\n - CMD-SHELL\n - curl -fsS http://127.0.0.1:8088/ >/dev/null\n timeout: 5s\n interval: 10s\n retries: 12\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 8088\n published: \"8088\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n - /run/dashboard-redis:size=16m,mode=0700\n volumes:\n - type: volume\n source: dashboard-redis\n target: /var/lib/dashboard-redis\n volume: {}\n - type: bind\n source: /proc/net/arp\n target: /host/proc/net/arp\n read_only: true\n bind:\n create_host_path: true\n miner-route:\n cap_add:\n - NET_ADMIN\n container_name: miner-route\n entrypoint:\n - /bin/sh\n - -c\n - 'while true; do if [ -n \"$$BDAG_MINER_ROUTE_SUBNET\" ] && [ -n \"$$BDAG_MINER_ROUTE_GATEWAY\" ]; then cmd=\"ip route replace $$BDAG_MINER_ROUTE_SUBNET via $$BDAG_MINER_ROUTE_GATEWAY\"; [ -n \"$$BDAG_MINER_ROUTE_DEV\" ] && cmd=\"$$cmd dev $$BDAG_MINER_ROUTE_DEV\"; $$cmd || echo \"miner-route: failed: $$cmd\"; fi; sleep 60; done'\n environment:\n BDAG_MINER_ROUTE_DEV: \"\"\n BDAG_MINER_ROUTE_GATEWAY: \"\"\n BDAG_MINER_ROUTE_SUBNET: \"\"\n image: alpine:3.20\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n restart: unless-stopped\n node:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: node\n cpu_shares: 4096\n container_name: node\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_ASIC_LAN_INTERFACE: enxc8a362eb4bdc\n BDAG_DETECTED_NETWORK_TOPOLOGY: \"\"\n BDAG_ENABLE_NODE_MINING: \"1\"\n BDAG_ENTRYPOINT_CHOWN_MODE: needed\n BDAG_EPHEMERAL_DIR: /run/bdag-ephemeral\n BDAG_NETWORK: \"\"\n BDAG_NETWORK_TOPOLOGY: auto\n BDAG_NODE_ARCHIVAL: \"0\"\n BDAG_NODE_DEBUG_LEVEL: warn\n BDAG_NODE_MINING_ARGS: \"\"\n BDAG_NODE_MINING_NO_PENDING_TX: \"0\"\n BDAG_NODE_MODULES: Blockdag,miner\n BDAG_NODE_NO_FILE_LOGGING: \"1\"\n BDAG_NODE_OBSOLETE_HEIGHT: \"20\"\n BDAG_NODEWORKER_LIVENESS_TIMEOUT: 5m\n BDAG_P2P_ADVERTISE_IP: \"\"\n BDAG_P2P_INTERFACE: \"\"\n BDAG_SNAPSHOT_URL: \"\"\n BDAG_STORAGE_PROFILE: auto\n BOOTSTRAP_PEER_ADDRESSES: /ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\n MINING_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_ARGS_APPEND: --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n P2P_PORT: \"8155\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n SYNC_SOURCE_NODE: \"0\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -900\n platform: linux/amd64\n restart: unless-stopped\n shm_size: \"536870912\"\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\n volumes:\n - type: bind\n source: /home/jeremy/blockdag-source/stack/data/node\n target: /var/lib/bdagStack/node\n bind:\n create_host_path: true\n - type: volume\n source: nodeworker-data\n target: /var/lib/bdagStack/nodeworker\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker\n target: /snapshot/latest.bdsnap\n read_only: true\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/node.conf\n target: /etc/bdagStack/node.conf\n read_only: true\n bind:\n create_host_path: true\n pool:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: pool\n cpu_shares: 3072\n container_name: pool\n depends_on:\n pool-db:\n condition: service_healthy\n required: true\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n METRICS_ADDR: 0.0.0.0:9090\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_URL: http://127.0.0.1:38131\n NODE_RPC_URLS: http://127.0.0.1:38131\n NODE_RPC_USER: test\n PG_URL: postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable\n POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB: \"true\"\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE: \"true\"\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS: \"8000\"\n POOL_BIND_ADDR: 192.168.1.100:3334\n POOL_BLOCK_MATURITY: \"10\"\n POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_ENABLED: \"true\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_STEP_MS: \"250\"\n POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT: \"5\"\n POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS: \"60\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: \"3\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_FEE_PERCENTAGE: \"1.0\"\n POOL_INSTANCE_ID: \"\"\n POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS: \"1750\"\n POOL_PAYOUT_MATURITY: \"4096\"\n POOL_PAYOUT_MAX_BLOCKS_PER_TICK: \"5\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED: \"false\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS: \"0\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED: \"true\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS: \"20\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS: \"1000\"\n POOL_PRIVATE_KEY: 11f16642b7153d33ef1f2029fad20e77489e38c801afe93ac5d1264238e6d780\n POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: \"1750\"\n POOL_RPC_BACKENDS: \"\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: \"false\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS: \"30\"\n POOL_RPC_ROUTER_EVM_REFERENCE_URLS: https://rpc.blockdag.engineering,https://rpc.bdagscan.com\n POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: \"true\"\n POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS: \"15\"\n POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS: \"30\"\n POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS: \"2\"\n POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS: \"15\"\n POOL_RUNTIME_ADMIN_ENABLED: \"false\"\n POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: \"0\"\n POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: \"1\"\n POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS: \"5\"\n POOL_STALE_RACE_REJECT_WINDOW_SECONDS: \"10\"\n POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS: \"60\"\n POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: \"true\"\n POOL_SUBMIT_RPC_URLS: \"\"\n POOL_SUBMIT_STALE_BLOCK_CANDIDATES: \"false\"\n POOL_TEMPLATE_TTL_REFRESH_MS: \"100\"\n PPLNS_N_WORK: \"1000\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n WALLET_RPC_URL: http://127.0.0.1:18545\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -800\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n pool-db:\n cpu_shares: 3072\n container_name: postgres\n environment:\n POSTGRES_DB: bdagpool\n POSTGRES_PASSWORD: 06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803\n POSTGRES_USER: bdag_pool\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n healthcheck:\n test:\n - CMD-SHELL\n - pg_isready -U bdag_pool -d bdagpool\n timeout: 5s\n interval: 5s\n retries: 20\n image: postgres:15-bookworm\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: -800\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 5432\n published: \"55432\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n volumes:\n - type: volume\n source: postgres-data\n target: /var/lib/postgresql/data\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/sql/pool-schema.sql\n target: /docker-entrypoint-initdb.d/01-pool-schema.sql\n read_only: true\n bind:\n create_host_path: true\n sentinel:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: sentinel\n cpu_shares: 256\n container_name: sentinel\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep $${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_SENTINEL_INTERVAL_SECONDS: \"60\"\n BDAG_SENTINEL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_SENTINEL_USER_SERVICES: \"\"\n BDAG_SENTINEL_USER_TIMERS: \"\"\n BDAG_STACK_SERVICES: postgres,node,pool\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n status-sampler:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: status-sampler\n blkio_config:\n weight: 100\n cpu_shares: 256\n container_name: status-sampler\n entrypoint:\n - /bin/sh\n - -c\n - exec python3 ops/status_sampler.py --loop --no-logs\n environment:\n BDAG_CHAIN_STATE_SELF_HEAL_COMMAND: ops/chain-state-self-heal.sh --from-systemd\n BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_CHAIN_STATE_SELF_HEAL_UNIT: \"\"\n BDAG_MINING_IMPERATIVE_GUARD_UNITS: \"\"\n BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS: \"30\"\n BDAG_NODE_RPC_URLS: node=http://host.docker.internal:38131\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS: \"60\"\n BDAG_STATUS_SAMPLER_FILE: /home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json\n BDAG_STATUS_SAMPLER_INTERVAL_SECONDS: \"10\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n watchdog:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: watchdog\n cpu_shares: 256\n container_name: watchdog\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST: 192.168.1.100\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SOURCE_URL: http://dashboard:8088/api/status\n BDAG_WATCHDOG_INTERVAL: \"60\"\n BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS: \"300\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\nnetworks:\n default:\n name: stack_default\nvolumes:\n dashboard-redis:\n name: stack_dashboard-redis\n nodeworker-data:\n name: stack_nodeworker-data\n postgres-data:\n name: stack_postgres-data\nx-build-args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\nx-dev-source-contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\nx-mining-logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\nx-node-critical:\n blkio_config:\n weight: 1000\n cpu_shares: 4096\n oom_score_adj: -900\n shm_size: 512m\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\nx-ops-low:\n blkio_config:\n weight: 100\n cpu_shares: 256\n oom_score_adj: 300\nx-pool-critical:\n blkio_config:\n weight: 900\n cpu_shares: 3072\n oom_score_adj: -800", + "repo_state": { + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "diff_stat": "", + "head": "7426d7fa1b39a29b57b5e81f24dc2baaede0c54c", + "last_commit": "7426d7f Protect mining health from zero coinbase templates", + "path": "/home/jeremy/blockdag-source/blockdag-corechain", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-corechain: codex post-savepoint zero-coinbase corechain patchset 20260621", + "status": "## codex/native-safe-mining-20260620-corechain...origin/codex/native-safe-mining-20260620-corechain [ahead 1]" + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "diff_stat": "internal/pool/server/jobBroadcast.go | 13 ++++++++-----\n internal/pool/server/job_broadcast_test.go | 12 ++++++------\n internal/pool/server/node_health.go | 23 ++++++++++++++++++++++-\n internal/pool/server/rpc_router_test.go | 10 +++++-----\n 4 files changed, 41 insertions(+), 17 deletions(-)", + "head": "11fa7a97e31be091cf64b1fa1ccdf12ceb70c92a", + "last_commit": "11fa7a9 Gate stale block submits on native health", + "path": "/home/jeremy/blockdag-source/pool", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-pool: codex post-savepoint zero-coinbase pool patchset 20260621", + "status": "## codex/native-safe-mining-20260620-pool...origin/codex/native-safe-mining-20260620-pool [ahead 1]\n M internal/pool/server/jobBroadcast.go\n M internal/pool/server/job_broadcast_test.go\n M internal/pool/server/node_health.go\n M internal/pool/server/rpc_router_test.go" + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "diff_stat": "main.go | 29 ++-\n templates.go | 550 ++++++++++++++++++++++++++++++++++++++++++++----------\n templates_test.go | 129 ++++++++++++-\n 3 files changed, 603 insertions(+), 105 deletions(-)", + "head": "6fb39a6aad053d0e3dc8e588cd54fed0b74d6162", + "last_commit": "6fb39a6 Expose mining template safety status", + "path": "/home/jeremy/blockdag-source/redis-dash", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-dashboard: codex post-savepoint dashboard safety patchset 20260621", + "status": "## codex/native-safe-mining-20260620-dashboard...origin/codex/native-safe-mining-20260620-dashboard [ahead 1]\n M main.go\n M templates.go\n M templates_test.go" + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "diff_stat": "", + "head": "00a67593451555e82ed5bf864668dfb1ff8811cf", + "last_commit": "00a6759 Harden redis stack mining safety defaults", + "path": "/home/jeremy/blockdag-source/stack-redis", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-stack: codex post-savepoint zero-coinbase/watchdog patchset 20260621", + "status": "## codex/native-safe-mining-20260620-stack...origin/codex/native-safe-mining-20260620-stack [ahead 2]" + } + }, + "rollback_commands": [ + "docker tag stack-dashboard:pre-dashboard-design-20260622-012730 stack-dashboard:latest && docker compose up -d --no-deps --no-build dashboard", + "docker compose stop pool && docker tag stack-pool:pre-node-sync-preserve-20260622-012730 stack-pool:latest && docker compose up -d --no-deps --no-build pool", + "docker tag stack-status-sampler:pre-dashboard-design-20260622-012730 stack-status-sampler:latest && docker compose up -d --no-deps --no-build status-sampler", + "If node rollback is ever required: stop pool first, restore node tag/config deliberately, verify native template health and fresh peers, then restart pool." + ], + "rollback_tags": { + "dashboard": "stack-dashboard:pre-dashboard-design-20260622-012730", + "node": "stack-node:pre-dashboard-design-20260622-012730", + "pool": "stack-pool:pre-node-sync-preserve-20260622-012730", + "status_sampler": "stack-status-sampler:pre-dashboard-design-20260622-012730" + }, + "savepoint_id": "20260622-012730-pre-dashboard-pool-deploy-retained" +} diff --git a/ops/savepoints/20260622-013454-post-sidebar-pool-working-retained.json b/ops/savepoints/20260622-013454-post-sidebar-pool-working-retained.json new file mode 100644 index 0000000..6b8ddbf --- /dev/null +++ b/ops/savepoints/20260622-013454-post-sidebar-pool-working-retained.json @@ -0,0 +1,7319 @@ +{ + "container_state": { + "dashboard": { + "image_latest_id": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/dashboard" + ], + "Env": [ + "BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS=10", + "BDAG_GLOBAL_FAST_WARMUP_BLOCKS=120", + "BDAG_TREND_BACKFILL_CHUNK_SIZE=128", + "BDAG_GLOBAL_SETTLE_DELAY_SECONDS=5", + "BDAG_REDIS_MAXMEMORY_POLICY=noeviction", + "BDAG_GLOBAL_MAX_INCREMENTAL_GAP=120", + "BDAG_REDIS_APPENDFSYNC=everysec", + "TEMP=/tmp", + "DASHBOARD_TIMEZONE=Africa/Johannesburg", + "NODE_RPC_PASS=", + "BDAG_REDIS_APPENDONLY=yes", + "BDAG_MINER_CONTROL_ENABLED=0", + "BDAG_CHAIN_POLL_SECONDS=2", + "BDAG_POOL_METRICS_URL=http://host.docker.internal:9090/metrics", + "BDAG_EVM_HTTP_URL=http://host.docker.internal:18545", + "BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS=2048", + "BDAG_REDIS_SOCKET=/run/dashboard-redis/redis.sock", + "BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS=", + "BDAG_EVM_SYNC_BACKOFF_SECONDS=60", + "TMPDIR=/tmp", + "BDAG_TREND_RPC_WORKERS=4", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS=3", + "BDAG_EVM_WS_URL=ws://host.docker.internal:18546", + "BDAG_TREND_BACKFILL_DAYS=30", + "BDAG_TREND_HISTORY_ENABLED=1", + "BDAG_GLOBAL_RPC_WORKERS=4", + "BDAG_TREND_BACKFILL_SLEEP_MS=100", + "BDAG_REDIS_MAXMEMORY=256mb", + "BDAG_REDIS_CONFIG=/etc/redis/redis.conf", + "BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30", + "BDAG_GLOBAL_RPC_CHUNK_SIZE=64", + "BDAG_TREND_CATCHUP_INTERVAL_SECONDS=30", + "NODE_RPC_USER=test", + "BDAG_TREND_RECENT_WARMUP_BLOCKS=3600", + "BDAG_NODE_RPC_URL=http://host.docker.internal:38131", + "BDAG_GLOBAL_BLOCK_WINDOW=600", + "BDAG_DASHBOARD_REDIS_MANAGED=1", + "TMP=/tmp", + "ADDR=0.0.0.0:8088", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_DASHBOARD_REDIS_ENABLED=1", + "BDAG_DASHBOARD_HEALTH_STALE_SECONDS=20", + "BDAG_POOL_URL=stratum+tcp://192.168.1.100:3334", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "BDAG_TREND_SPARSE_SAMPLES_PER_PASS=", + "POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp", + "BDAG_CHAIN_WS_READ_LIMIT_BYTES=1048576", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "8088/tcp": {} + }, + "Healthcheck": { + "Interval": 10000000000, + "Retries": 12, + "Test": [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:8088/ >/dev/null" + ], + "Timeout": 5000000000 + }, + "Hostname": "4dcde8c64f82", + "Image": "stack-dashboard", + "Labels": { + "com.docker.compose.config-hash": "e03f4a8bb3888d03330d9472539c32e0a4893687873b226ce12c92c9412a2162", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "dashboard", + "com.docker.compose.service": "dashboard", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "" + }, + "Created": "2026-06-21T23:29:41.860726868Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "stack_dashboard-redis:/var/lib/dashboard-redis:rw", + "/proc/net/arp:/host/proc/net/arp:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/run/dashboard-redis": "size=16m,mode=0700", + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/hostname", + "HostsPath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/hosts", + "Id": "4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425", + "Image": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "ImageManifestDescriptor": { + "digest": "sha256:357094195a3d5564faa8c789ec583186a33793bb80175940a123358c6df9ea79", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 1242 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/lib/dashboard-redis", + "Driver": "local", + "Mode": "rw", + "Name": "stack_dashboard-redis", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_dashboard-redis/_data", + "Type": "volume" + }, + { + "Destination": "/host/proc/net/arp", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/proc/net/arp", + "Type": "bind" + } + ], + "Name": "/dashboard", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "dashboard", + "dashboard" + ], + "DNSNames": [ + "dashboard", + "4dcde8c64f82" + ], + "DriverOpts": null, + "EndpointID": "ae134f93be390b4a1adedfc1e1a4211e470561379eab34566e96b48f174c09f9", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.4", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "5e:6f:a7:ee:14:dd", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "SandboxID": "ff534d22b1daa3f71f8ed45a63af8bdd3e152ec291ba7f3ad157416a9575294d", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/dashboard", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "Health": { + "FailingStreak": 0, + "Log": [ + { + "End": "2026-06-22T01:35:11.847128259+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:35:09.596970868+02:00" + }, + { + "End": "2026-06-22T01:35:21.924921823+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:35:21.849252481+02:00" + }, + { + "End": "2026-06-22T01:35:31.986905509+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:35:31.925944389+02:00" + }, + { + "End": "2026-06-22T01:35:42.047155942+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:35:41.987881579+02:00" + }, + { + "End": "2026-06-22T01:35:52.808998419+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T01:35:52.047880061+02:00" + } + ], + "Status": "healthy" + }, + "OOMKilled": false, + "Paused": false, + "Pid": 3624915, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T23:29:43.671184302Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "15:C 21 Jun 2026 23:29:43.823 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo\n15:C 21 Jun 2026 23:29:43.823 # Redis version=7.0.15, bits=64, commit=00000000, modified=0, pid=15, just started\n15:C 21 Jun 2026 23:29:43.823 # Configuration loaded\n15:M 21 Jun 2026 23:29:43.823 * Increased maximum number of open files to 10032 (it was originally set to 1024).\n15:M 21 Jun 2026 23:29:43.823 * monotonic clock: POSIX clock_gettime\n15:M 21 Jun 2026 23:29:43.824 * Running mode=standalone, port=0.\n15:M 21 Jun 2026 23:29:43.824 # Server initialized\n15:M 21 Jun 2026 23:29:43.825 * Reading RDB base file on AOF loading...\n15:M 21 Jun 2026 23:29:43.825 * Loading RDB produced by version 7.0.15\n15:M 21 Jun 2026 23:29:43.825 * RDB age 1916 seconds\n15:M 21 Jun 2026 23:29:43.825 * RDB memory usage when created 11.58 Mb\n15:M 21 Jun 2026 23:29:43.825 * RDB is base AOF\n15:M 21 Jun 2026 23:29:43.863 * Done loading RDB, keys loaded: 2837, keys expired: 0.\n15:M 21 Jun 2026 23:29:43.863 * DB loaded from base file appendonly.aof.132.base.rdb: 0.039 seconds\n15:M 21 Jun 2026 23:29:43.951 * DB loaded from incr file appendonly.aof.132.incr.aof: 0.088 seconds\n15:M 21 Jun 2026 23:29:43.951 * DB loaded from append only file: 0.127 seconds\n15:M 21 Jun 2026 23:29:43.952 * Opening AOF incr file appendonly.aof.132.incr.aof on server start\n15:M 21 Jun 2026 23:29:43.952 * The server is now ready to accept connections at /run/dashboard-redis/redis.sock\n2026/06/21 23:29:43 dashboard listening on http://127.0.0.1:8088 using collector API http://127.0.0.1:9280 display timezone Africa/Johannesburg\n2026/06/21 23:30:07 live trend gap at 11858008 after 11854520; rebuilding recent trend window\n2026/06/21 23:30:23 global Redis window source height moved backward from 11858017 to 11779165; rebuilding bounded live window\n2026/06/21 23:30:39 recent trend warmup failed: fetch 11854673-11854800: eth_getBlockByNumber(0xb4e354) returned null; eth_getBlockByNumber(0xb4e353) returned null; eth_getBlockByNumber(0xb4e351) returned null; eth_getBlockByNumber(0xb4e352) returned null; eth_getBlockByNumber(0xb4e355) returned null; eth_getBlockByNumber(0xb4e356) returned null; eth_getBlockByNumber(0xb4e357) returned null; eth_getBlockByNumber(0xb4e358) returned null; eth_getBlockByNumber(0xb4e35b) returned null; eth_getBlockByNumber(0xb4e35c) returned null; eth_getBlockByNumber(0xb4e35a) returned null; eth_getBlockByNumber(0xb4e359) returned null; eth_getBlockByNumber(0xb4e360) returned null; eth_getBlockByNumber(0xb4e35d) returned null; eth_getBlockByNumber(0xb4e35f) returned null; eth_getBlockByNumber(0xb4e35e) returned null; eth_getBlockByNumber(0xb4e361) returned null; eth_getBlockByNumber(0xb4e363) returned null; eth_getBlockByNumber(0xb4e362) returned null; eth_getBlockByNumber(0xb4e364) returned null; eth_getBlockByNumber(0xb4e365) returned null; eth_getBlockByNumber(0xb4e366) returned null; eth_getBlockByNumber(0xb4e368) returned null; eth_getBlockByNumber(0xb4e367) returned null; eth_getBlockByNumber(0xb4e36b) returned null; eth_getBlockByNumber(0xb4e36a) returned null; eth_getBlockByNumber(0xb4e369) returned null; eth_getBlockByNumber(0xb4e36c) returned null; eth_getBlockByNumber(0xb4e36d) returned null; eth_getBlockByNumber(0xb4e36e) returned null; eth_getBlockByNumber(0xb4e370) returned null; eth_getBlockByNumber(0xb4e36f) returned null; eth_getBlockByNumber(0xb4e371) returned null; eth_getBlockByNumber(0xb4e373) returned null; eth_getBlockByNumber(0xb4e372) returned null; eth_getBlockByNumber(0xb4e374) returned null; eth_getBlockByNumber(0xb4e377) returned null; eth_getBlockByNumber(0xb4e375) returned null; eth_getBlockByNumber(0xb4e376) returned null; eth_getBlockByNumber(0xb4e378) returned null; eth_getBlockByNumber(0xb4e37a) returned null; eth_getBlockByNumber(0xb4e37c) returned null; eth_getBlockByNumber(0xb4e379) returned null; eth_getBlockByNumber(0xb4e37b) returned null; eth_getBlockByNumber(0xb4e37d) returned null; eth_getBlockByNumber(0xb4e380) returned null; eth_getBlockByNumber(0xb4e37e) returned null; eth_getBlockByNumber(0xb4e37f) returned null; eth_getBlockByNumber(0xb4e381) returned null; eth_getBlockByNumber(0xb4e382) returned null; eth_getBlockByNumber(0xb4e384) returned null; eth_getBlockByNumber(0xb4e383) returned null; eth_getBlockByNumber(0xb4e385) returned null; eth_getBlockByNumber(0xb4e386) returned null; eth_getBlockByNumber(0xb4e387) returned null; eth_getBlockByNumber(0xb4e388) returned null; eth_getBlockByNumber(0xb4e38a) returned null; eth_getBlockByNumber(0xb4e389) returned null; eth_getBlockByNumber(0xb4e38b) returned null; eth_getBlockByNumber(0xb4e38c) returned null; eth_getBlockByNumber(0xb4e38d) returned null; eth_getBlockByNumber(0xb4e38e) returned null; eth_getBlockByNumber(0xb4e38f) returned null; eth_getBlockByNumber(0xb4e390) returned null; eth_getBlockByNumber(0xb4e392) returned null; eth_getBlockByNumber(0xb4e391) returned null; eth_getBlockByNumber(0xb4e393) returned null; eth_getBlockByNumber(0xb4e394) returned null; eth_getBlockByNumber(0xb4e397) returned null; eth_getBlockByNumber(0xb4e396) returned null; eth_getBlockByNumber(0xb4e395) returned null; eth_getBlockByNumber(0xb4e398) returned null; eth_getBlockByNumber(0xb4e39b) returned null; eth_getBlockByNumber(0xb4e399) returned null; eth_getBlockByNumber(0xb4e39a) returned null; eth_getBlockByNumber(0xb4e39c) returned null; eth_getBlockByNumber(0xb4e39f) returned null; eth_getBlockByNumber(0xb4e39e) returned null; eth_getBlockByNumber(0xb4e39d) returned null; eth_getBlockByNumber(0xb4e3a0) returned null; eth_getBlockByNumber(0xb4e3a4) returned null; eth_getBlockByNumber(0xb4e3a1) returned null; eth_getBlockByNumber(0xb4e3a3) returned null; eth_getBlockByNumber(0xb4e3a2) returned null; eth_getBlockByNumber(0xb4e3a5) returned null; eth_getBlockByNumber(0xb4e3a8) returned null; eth_getBlockByNumber(0xb4e3a6) returned null; eth_getBlockByNumber(0xb4e3a7) returned null; eth_getBlockByNumber(0xb4e3a9) returned null; eth_getBlockByNumber(0xb4e3aa) returned null; eth_getBlockByNumber(0xb4e3ab) returned null; eth_getBlockByNumber(0xb4e3ac) returned null; eth_getBlockByNumber(0xb4e3af) returned null; eth_getBlockByNumber(0xb4e3b0) returned null; eth_getBlockByNumber(0xb4e3ae) returned null; eth_getBlockByNumber(0xb4e3ad) returned null; eth_getBlockByNumber(0xb4e3b2) returned null; eth_getBlockByNumber(0xb4e3b1) returned null; eth_getBlockByNumber(0xb4e3b3) returned null; eth_getBlockByNumber(0xb4e3b4) returned null; eth_getBlockByNumber(0xb4e3b7) returned null; eth_getBlockByNumber(0xb4e3b8) returned null; eth_getBlockByNumber(0xb4e3b5) returned null; eth_getBlockByNumber(0xb4e3b6) returned null; eth_getBlockByNumber(0xb4e3b9) returned null; eth_getBlockByNumber(0xb4e3bc) returned null; eth_getBlockByNumber(0xb4e3bb) returned null; eth_getBlockByNumber(0xb4e3ba) returned null; eth_getBlockByNumber(0xb4e3c0) returned null; eth_getBlockByNumber(0xb4e3be) returned null; eth_getBlockByNumber(0xb4e3bd) returned null; eth_getBlockByNumber(0xb4e3bf) returned null; eth_getBlockByNumber(0xb4e3c1) returned null; eth_getBlockByNumber(0xb4e3c2) returned null; eth_getBlockByNumber(0xb4e3c3) returned null; eth_getBlockByNumber(0xb4e3c4) returned null; eth_getBlockByNumber(0xb4e3c7) returned null; eth_getBlockByNumber(0xb4e3c5) returned null; eth_getBlockByNumber(0xb4e3c6) returned null; eth_getBlockByNumber(0xb4e3c8) returned null; eth_getBlockByNumber(0xb4e3c9) returned null; eth_getBlockByNumber(0xb4e3ca) returned null; eth_getBlockByNumber(0xb4e3cb) returned null; eth_getBlockByNumber(0xb4e3cc) returned null; eth_getBlockByNumber(0xb4e3cd) returned null; eth_getBlockByNumber(0xb4e3ce) returned null; eth_getBlockByNumber(0xb4e3cf) returned null; eth_getBlockByNumber(0xb4e3d0) returned null\n2026/06/21 23:32:23 global Redis window detected reorg near height 11779302; rebuilding rolling window", + "ports": "8088/tcp -> 127.0.0.1:8088" + }, + "node": { + "image_latest_id": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Env": [ + "BDAG_ENTRYPOINT_CHOWN_MODE=needed", + "BDAG_NODE_MINING_NO_PENDING_TX=0", + "TEMP=/tmp", + "BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "TMP=/tmp", + "BDAG_NODE_MODULES=Blockdag,miner", + "MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m", + "BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc", + "SYNC_SOURCE_NODE=0", + "BDAG_NODE_ARCHIVAL=0", + "BDAG_SNAPSHOT_URL=", + "P2P_PORT=8155", + "BDAG_STORAGE_PROFILE=auto", + "BDAG_DETECTED_NETWORK_TOPOLOGY=", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_ENABLE_NODE_MINING=1", + "BDAG_NODE_NO_FILE_LOGGING=1", + "BDAG_NETWORK_TOPOLOGY=auto", + "BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP", + "BDAG_P2P_ADVERTISE_IP=", + "BDAG_P2P_INTERFACE=", + "BDAG_NODE_MINING_ARGS=", + "TMPDIR=/tmp", + "BDAG_NODE_OBSOLETE_HEIGHT=20", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODE_DEBUG_LEVEL=debug", + "BDAG_NETWORK=", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "18545/tcp": {}, + "18546/tcp": {}, + "38131/tcp": {}, + "38132/tcp": {}, + "6060/tcp": {}, + "8150/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-node", + "Labels": { + "com.docker.compose.config-hash": "cd2bed17cbd5f264367a3668eb1187c214cd03132542f8169834b50d2038d943", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "node", + "com.docker.compose.service": "node", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/node" + }, + "Created": "2026-06-21T22:27:14.249227789Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/home/jeremy/blockdag-source/stack-redis/node.conf:/etc/bdagStack/node.conf:ro", + "/home/jeremy/blockdag-source/stack/data/node:/var/lib/bdagStack/node:rw", + "stack_nodeworker-data:/var/lib/bdagStack/nodeworker:rw", + "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker:/snapshot/latest.bdsnap:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 4096, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -900, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 536870912, + "Tmpfs": { + "/run/bdag-ephemeral": "size=512m,mode=1777", + "/tmp": "size=512m,mode=1777", + "/var/tmp": "size=512m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hostname", + "HostsPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hosts", + "Id": "98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e", + "Image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "ImageManifestDescriptor": { + "digest": "sha256:7ffcf30b8a85bad3b77a4437afbd2c45d249c87a1fed5f0b6203dd2a6b5ffc6e", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2564 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/lib/bdagStack/nodeworker", + "Driver": "local", + "Mode": "rw", + "Name": "stack_nodeworker-data", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_nodeworker-data/_data", + "Type": "volume" + }, + { + "Destination": "/snapshot/latest.bdsnap", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker", + "Type": "bind" + }, + { + "Destination": "/etc/bdagStack/node.conf", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "Type": "bind" + }, + { + "Destination": "/var/lib/bdagStack/node", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack/data/node", + "Type": "bind" + } + ], + "Name": "/node", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "6a9346674bfadb8c19a03ff52c78803f32a10c9ad091efa7164f99827c3e3fb2", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "747be80cabc6764f4fc40a1e1dcdd2f581cbcbb8c329b64bf5e46e74c31dd0d2", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3534321, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T22:27:16.468444346Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9df769..e71d09 from=0x8eb7B00303296b4c27148FF399000CDB3573c00c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=048396..262e7d from=0x47C9535f0634763af68cbdD3dE1c5e7b950F19A0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=98a66c..2cfdf4 from=0x8DaAD79DB3D2a351e9330C2A374145B02E2Fe66D err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3b0559..55c6b1 from=0x010172CA1761778B5a7caA38014dB341d2A0eaE0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8c0e9f..df22c2 from=0x9B7f701560E0708c49aeE25487811b122f4c2151 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6ddefd..e619a9 from=0xDd4E58691C4c87849Dca407602773b42eBdC38BC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f28f9f..a5c8be from=0x39c10d70bc363E2552aBEe500C113775e8D90C85 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1491d7..c6cd01 from=0x9E41245AE3aF2952a2208Df99326F5c9A5F15c67 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3a5728..7cd89d from=0xDF0Ae9796DC1d6B59faDD144089FaD349F3b6dB9 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2702ba..6f002f from=0xfa195a9C9654563389E0246960580761e679EAC4 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=92dc72..9c365a from=0x205C61B5C4A47b9d341863d817ebA5369D4bed15 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=82708c..4713a8 from=0x5eACc0f51cC6387875bE1FC8dBa707c5BB5b4026 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f58d8f..3b6cff from=0x718b1890A074B4A730326eD63D6aF16FaDD88C28 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=d1cad9..a3dadd from=0xd6d074268AC528aAA80BE7cBA0c4aeE7eDE95ea9 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=297791..94aa43 from=0x50096E9cd55a3eF50D1103AFB926FFcD5c83Fea0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=0b3c45..57b0ff from=0xd437383F0BaE24165b58e679B228067b4D8Ea2Fe err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b01ab6..78018a from=0xe0A259751E549abE601A85E74f1beE46f1D39BB0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9825d7..350a37 from=0x329c367BdfcF80c296af1Ed63C3Ae590Fc311508 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1e3369..e4a1a6 from=0x81Dd9d105924c3beB1610145cd21f2C712257067 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1b5e84..34b25b from=0xF42e6Bc68ECA5AD7E719d8876AE544282c7458d2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f8acf1..022283 from=0xbAadc186AD1b0935b523C3D04ee52868b4641F1b err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.802 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c23c4c..c37a56 from=0x448B68c13863589FbE322DCCC986557fBFf8a440 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ecb559..01bad6 from=0xfaef2FE87B66B9FAee730D6a2158109303Bb08B3 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2057ec..33b58b from=0x9b9f6Bd6A84ddB75630667460BF9740f187Cf77c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5d9adb..0d9775 from=0x864806c30992b7B24C2BFDF79a35293A9f5c8111 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=fa1878..5f6c80 from=0x564a13E53Dbb07DFE90Aa21944B854f8693e2356 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7f877c..827237 from=0x83fd5B24fE8350db41E7BABd49bD95563Bfb88a7 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=772c15..2366db from=0xE59DD6c972f1aF0bB48Fd14CEF99C8ee62a708E1 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b883cc..f83554 from=0x92277561655C2E833F78f848C1c904522122f447 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c3cd9c..5446a5 from=0xC0d192dd025eF2730Ab6Ec5A7299A8C98329e611 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=756948..0b06c6 from=0x5941d1732a6550bea57D5090FA87BEc3582Dc44c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=bb21b1..1c07be from=0xE254bc431a364417B02a8cf3a302cA72928E869F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8b3caf..c8b44f from=0x2e19646b84d2A383eA9d4b51B868531c34E8B8fc err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6f276c..8a8183 from=0x562D25D81330B656ac9E410261fcA5Fe9BcBa6A5 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4a2cf7..29b7fc from=0x157493649C0132776E1595c7e6F5D98B583aa236 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=bbb685..004f3b from=0x6ACCf2BaEDA04688AfeD21b544fF1678da611Bbb err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c65cd0..a4c6cd from=0x03D6DC13aDA868109A00947608d1210Ceb7F1662 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5b9ab9..572a02 from=0x8De511c44b8A77eBd84488E5032EFeb595B4dE28 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=07dbb1..26c61b from=0x108bA846eeaAEd447E2c69918e3665dcaaC2Cdd1 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a82815..7fa538 from=0xcEb35B92792EA4D54D7f78384767fFD217477111 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=036f85..65c020 from=0x6DdeAA69A0d7C88d0B9ac77A172A8868f49700cB err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8ddea9..065249 from=0x563A50AE04adFAe6c7949649376381cf2032b8cC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2ea66c..454f4e from=0xbBe7AB304c479DA50A3e669642e1b24f81f83d21 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4f8eb5..57dfd9 from=0x69fB77B198F3e136fb6b563a0291a2183cAEAeDe err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=236468..6fca3a from=0x32C35d0bB937474B1D5765B088466eF50E12d10d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=344c77..2d2c6e from=0xb1335B7b9b4E5d9B49Fd32dE0E64450851ED92E2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5c5041..bffca0 from=0x5F0e335a28a8E41d47b4CB4c8cc7Ed44A49b7AfF err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9267f3..9e3d2f from=0xaA36CF946C24d7f6Aa03C5215166cA15Fb101134 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c162dc..466147 from=0x8F9c3f1113312e3d72ED191A03fF1bc461b76240 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=e82c00..6af033 from=0xEdb0a6d0636fc1Ff7e09c2ea2E76DAB400c1790C err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6901dc..93f88b from=0x2bFAb2358e406295A8597B3B09B6EcB178A29110 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.803 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6c48f3..665b4d from=0x357Cd083b70a5c46d8E5C66bF472A0Bc1f23D1F8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6b2f14..6a936c from=0x8C4a200C68ce41aC785cDD4191cDA5B7b7F92D06 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=d56977..72846a from=0x3bcfBb999f0A5e7835A6EDE3F081B4f9d4Eb178d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=16e773..4ebb98 from=0x73Be4d40478156621A2C521282A4e7aAc09636dC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8f394b..0a744a from=0x3953218fE81EfbcB23728B82FC884B526738543b err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=211a2f..f93d69 from=0x7CD9557b07ba49e488D092FbbD963D581005434d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f0aff4..31c48a from=0x5F0c89A367876143e216610771A946C0D9Ed21Cd err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=76afe3..ab317f from=0x36d9eCd641197bc32B37Cc5e20514CeaaC9E5101 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=52fd86..1d6e70 from=0x8FEB33ac99F4B63d169329842E5471a8D5B77b99 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a83cf8..a3be4f from=0xCAc7Ed66ab72fcE5Cb96744C68B967CF72ab562F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8cccd1..f4ff0a from=0xba17caB7C18B908820F1458855372b39753E4672 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=85d24d..bff599 from=0xc3053bC26763cC44F0431aF4be5B25156A5e2DC0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8a746b..c2eff7 from=0x25D98A6f653d969a0F4A1F13B63355426fB93235 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9d3a66..c581bb from=0x06ab4D265974BD0e4c48d2dD713EdDfEecD64FA2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=96d63a..7116c8 from=0x312C090a849FdeE8D5cC0f34F2a1cB34955a9092 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f2f483..0266f4 from=0x2daBc83A51a312a839c16574f4E27AA68CB8A8C8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1a9055..b89cd9 from=0x83E9C2DC58E8dcd7559CB3630d0Ad24Bec51b13c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ec1eae..c18568 from=0xb8485d6693AB4fF7BD403868208f888Fc4dE656f err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7da959..cc3ba3 from=0xAfaFd70c3d150718FF69A27Dac891fE41fEb47f5 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=53e26a..7e1630 from=0x86fbA9C0b0C7b0FD7c9EB9fe6A1b991329690a3F err=\"address is blocked by on-chain governance blocklist: recipient 0xF1c4393Fc08A965034063D637E3C965048A0e5f2\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4acce7..cf838f from=0x08b68908cE4a389Ed10EDf6e149eA2eae1Ba4968 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=475d14..873f91 from=0x96A4d857c46D1e8c23E1a002189acf530aC128f7 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4ba395..4959c7 from=0xD3A58bDB6C80E2B331a3f89811ea4B642be32446 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a9f3f6..f6b49f from=0x73EC36279f9A774DAabf204AE4d97F827a4DfCA8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ffb6dd..bca92d from=0x6CE262412f436A0a8246FA2Cf70DAE779B218126 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a41ff1..a5fc9b from=0xcF65d023346C10f8CA6B097B3479A532476b5645 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6a4e3d..99bbb8 from=0x9252657c7db9f116586a75E95D788545D96A38EF err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.804 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b05100..d1c3aa from=0x9F1158F81E2EaDc0Ce0D490eE64703207DA9dE3e err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ab6640..14d043 from=0x20d7D512aC4C885b12145f47c653fb5bd1f1B3dC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=493e91..23c3d4 from=0xc2e0E98Cb12F919ae1206faAc2C5a856aE6c7Cb2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a2c1b5..1d766a from=0x769656a9A24483bEb75d85574130DF6490853687 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=07a2ca..cd2c27 from=0xAC02a613C816d53e97331ec8E6c11eC787199A7d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a83d2c..b7a451 from=0x6FF39356A422CE91e278F0aB024DCb81D842fd93 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ac17c0..102fe0 from=0x4a2b631957c48e9E3b6A50d623e0A9E16325b453 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1b0ec4..1bb2a8 from=0xA6Bc27Dd5C3b5f67a3f8272854E37Cc79Bf66138 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b14e0e..d4899e from=0xbE51a3eaD791FAd33C6D8D648529b559Aed54597 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=e1dd19..16fa85 from=0x4813d5f4792B85b33BB8b58BA1e74c39e7A07976 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=04ae0a..dce647 from=0x2bfcB88ad3f71b6279d2ee0eFa4B215A9e5B44aA err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6d514b..7efba1 from=0x958ed7317d7a12D9211d29c60077B3f14EC76837 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3cb531..dcfc5c from=0xC0c7082379E68004A9746CE3a5F1768ec8DEd33F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5dd78b..5fae00 from=0x8fc52379b14a43CB9cAD764D93627D7DDaa30795 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1ae958..f91920 from=0x157f687727A7cDE9c674dEb7e5b2b047ABE51Ec4 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7e8293..c113ac from=0x93b2E311805D6c6C4A83b10D4CB4302ae79663Fb err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.805 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=74bd26..3e1555 from=0xB64F7Eb749166696A27182dFa48B1806Bc98Ce99 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-21|23:35:52.806 [DEBUG] Update BdagPool module=BDAG number=11840498 txs=0 gas=0 fees=0 elapsed=8.478ms\n2026-06-21|23:35:52.806 [INFO ] update BdagPool snapshot module=BDAG size=0\n2026-06-21|23:35:52.868 [DEBUG] Accepted block module=CHAIN hash=0x2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818 age=2.868029977s\n2026-06-21|23:35:52.868 [DEBUG] Auto-staked 7330380572400000000 BDAG (minted 383762316138092 Shares) for 0x1719E0ee598c15957448D5E568948101DF78e7A0 in Epoch 19. module=bdag/consensus\n2026-06-21|23:35:52.868 [DEBUG] End RPC Call (id:&1 method:Blockdag_SubmitBlockHeaderV2) module=RPC\n2026-06-21|23:35:52.868 [DEBUG] request handled module=gateway method=submitBlockHeaderV2 target= duration=144.936365ms\n2026-06-21|23:35:52.868 [DEBUG] Template parent changed module=miner old=0x1f1bfd9f10a325... new=0xf596108a8dcdda... reason=parent_changed\n2026-06-21|23:35:52.868 [DEBUG] gbtdetail module=miner txcount=0 parents=\"[{Data:23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a Hash:0x23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a} {Data:c7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac Hash:0xc7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac} {Data:35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710 Hash:0x35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710} {Data:2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818 Hash:0x2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818}]\" prehash=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952\n2026-06-21|23:35:52.868 [DEBUG] gbtend module=miner txcount=0 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952 spent=0\n2026-06-21|23:35:52.868 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:35:52.868 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=109.5665ms\n2026-06-21|23:35:52.868 [DEBUG] gbtdetail module=miner txcount=0 parents=\"[{Data:23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a Hash:0x23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a} {Data:c7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac Hash:0xc7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac} {Data:35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710 Hash:0x35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710} {Data:2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818 Hash:0x2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818}]\" prehash=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952\n2026-06-21|23:35:52.868 [DEBUG] gbtend module=miner txcount=0 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952 spent=0\n2026-06-21|23:35:52.868 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:35:52.868 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=88.203421ms\n2026-06-21|23:35:52.869 [DEBUG] Start RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:35:52.869 [DEBUG] gbtstart module=miner\n2026-06-21|23:35:52.869 [DEBUG] gbtdetail module=miner txcount=0 parents=\"[{Data:23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a Hash:0x23821df59bcaf3cd01d9ad0696d9892c6343c8c826eabdf755a42029a16f211a} {Data:c7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac Hash:0xc7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac} {Data:35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710 Hash:0x35a4de23c48acacc8eac0ef864744df89196c56d89ffef57f85f7566e7a32710} {Data:2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818 Hash:0x2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818}]\" prehash=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952\n2026-06-21|23:35:52.869 [DEBUG] gbtend module=miner txcount=0 longpollid=0xf596108a8dcdda6382d7ad724bee039a8240cdecd4e3f1f53a510d879776a791-1782084952 spent=0\n2026-06-21|23:35:52.869 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-21|23:35:52.869 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=435.189\u00b5s\n2026-06-21|23:35:53.015 [DEBUG] request handled module=gateway method=eth_getBlockByNumber target=5.78.193.245:18545 duration=287.515336ms\n2026-06-21|23:35:53.016 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11840497 maxHeight=11858472\n2026-06-21|23:35:53.016 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545 host=5.78.193.245 height=11779559 latency=1.105889337s synced=true active=true\n2026-06-21|23:35:53.017 [DEBUG] using best peer module=gateway target=http://5.78.193.245:18545", + "ports": "" + }, + "pool": { + "image_latest_id": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/mining-pool" + ], + "Env": [ + "POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30", + "POOL_BLOCK_TIMING_CONTROLLER_ENABLED=true", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "POOL_INSTANCE_ID=", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0", + "POOL_SUBMIT_RPC_URLS=", + "POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true", + "POOL_PRIVATE_KEY=", + "POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com", + "POOL_STALE_RACE_REJECT_WINDOW_SECONDS=10", + "POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS=60", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES=20", + "TMP=/tmp", + "POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15", + "POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20", + "POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30", + "POOL_TEMPLATE_TTL_REFRESH_MS=100", + "POOL_RPC_BACKENDS=", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750", + "POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS=20", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "TMPDIR=/tmp", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "WALLET_RPC_URL=http://127.0.0.1:18545", + "POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT=5", + "PG_URL=postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_BIND_ADDR=192.168.1.100:3334", + "POOL_BLOCK_MATURITY=10", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3", + "POOL_RUNTIME_ADMIN_ENABLED=false", + "POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15", + "POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5", + "POOL_BLOCK_TIMING_CONTROLLER_STEP_MS=250", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750", + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0", + "POOL_PAYOUT_MATURITY=4096", + "POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true", + "POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60", + "TEMP=/tmp", + "NODE_RPC_USER=test", + "NODE_RPC_URLS=http://127.0.0.1:38131", + "METRICS_ADDR=0.0.0.0:9090", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750", + "POOL_FEE_PERCENTAGE=1.0", + "POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "NODE_RPC_URL=http://127.0.0.1:38131", + "NODE_RPC_PASS=", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000", + "PPLNS_N_WORK=1000", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "3334/tcp": {}, + "8080/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-pool", + "Labels": { + "com.docker.compose.config-hash": "3cc1bff3269cba209e027936c6f19ed844abc881d2e10bd0111807726ea628d1", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "pool", + "com.docker.compose.service": "pool", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "bdagStack", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/pool" + }, + "Created": "2026-06-21T23:31:44.333651017Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 3072, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -800, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/hostname", + "HostsPath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/hosts", + "Id": "af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d", + "Image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "ImageManifestDescriptor": { + "digest": "sha256:650c2c542fd237362f559231bc8af2b7cce67dbef9b45a8e7775724cf5f539ed", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2000 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [], + "Name": "/pool", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "368bdc5ba2b6dc822d00519c5907e1128f7a12b3c35dfc97aba9cb1a83261de3", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "d32ed26490b4ca57aa26dbd57bd1c13e39623c9315a68b7acf8007a4e6f3f9fd", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/mining-pool", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3631792, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T23:31:44.6094248Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026/06/21 23:35:30 \ud83d\udcb0 Block 76663407c1a7579aec2e941a1081909946571bf963bdd435ba71742a03000000 marked as PAID\n2026/06/21 23:35:30 \ud83d\udcb8 Processing Payouts for Block f9e1d6b639f0555b4e86be49c8ada864cc5cd2e112ce90d49008253702000000 (1 credits)\n2026/06/21 23:35:31 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:31 \ud83c\udfaf BLOCK FOUND height(le)=9348013 job=341-18bb3ccd60dedf43_06000000 hash=3703ddde848447804e7215be695c143251421778ee71b15b54e20f3301000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:31 \u2705 Block submitted successfully: map[accepted:true block_hash:0xff1c9d51c3bd90681139f4491bfe83b085d1a42c4b382ff2beadce2ef643ad70 code:accepted coinbase_amount:10 coinbase_txid: height:9.348013e+06 message:accepted miner_type:GBT_Worker order:12207682 template_seq:3839]\n2026/06/21 23:35:31 [REFRESH] accepted block parent advance (seq=341 parent=ff1c9d51c3bd90681139f4491bfe83b085d1a42c4b382ff2beadce2ef643ad70 suppressed=1)\n2026/06/21 23:35:31 \ud83d\udcb0 CREDIT SUMMARY block=3703ddde848447804e7215be695c143251421778ee71b15b54e20f3301000000 height=9348013 totalWork=49511357225 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348023 payoutUnlockHeight=9352109 suppressedDetailBlocks=0\n2026/06/21 23:35:31 [REFRESH] ws TTL refresh (seq=342 parent=ff1c9d51c3bd90681139f4491bfe83b085d1a42c4b382ff2beadce2ef643ad70)\n2026/06/21 23:35:31 [REFRESH] block submission accepted (seq=343 parent=ff1c9d51c3bd90681139f4491bfe83b085d1a42c4b382ff2beadce2ef643ad70 suppressed=1)\n2026/06/21 23:35:31 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:31 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:31 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x9de12ebc1ce2d96d0d8f487043b2e44266410df038346c4db2a1328a93982052)\n2026/06/21 23:35:31 \ud83d\udcb0 Block f9e1d6b639f0555b4e86be49c8ada864cc5cd2e112ce90d49008253702000000 marked as PAID\n2026/06/21 23:35:31 Payout: deferred remaining mature payout blocks after reaching POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5 suppressed=4\n2026/06/21 23:35:31 [vardiff] 192.168.1.102:47956 increase pdiff 0.115413 \u2192 0.138583 (shares=29 expired=0 in 49s, target=14/60s)\n2026/06/21 23:35:31 [vardiff] 192.168.1.102:47956 \u2192 pdiff 0.13858264\n2026/06/21 23:35:31 PUSHDIF -> 192.168.1.102:47956 mining.set_difficulty 0.13858264\n2026/06/21 23:35:32 \ud83c\udfaf BLOCK FOUND height(le)=9348014 job=343-18bb3ccd9b9d0f50_06000000 hash=0aa562b858ed928a93db89b1938d78986b0707b8c091e7df923aa43901000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:32 \u2705 Block submitted successfully: map[accepted:true block_hash:0xc494ad73e9ab1c41388938a85cfab93edcfbafddaa0f68010494d7a5da64d205 code:accepted coinbase_amount:10 coinbase_txid: height:9.348014e+06 message:accepted miner_type:GBT_Worker order:12207683 template_seq:3840]\n2026/06/21 23:35:32 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:32 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:32 \ud83d\udcb0 CREDIT SUMMARY block=0aa562b858ed928a93db89b1938d78986b0707b8c091e7df923aa43901000000 height=9348014 totalWork=50193759421 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348024 payoutUnlockHeight=9352110 suppressedDetailBlocks=0\n2026/06/21 23:35:33 [vardiff] 192.168.1.14:40389 increase pdiff 0.113826 \u2192 0.140387 (shares=29 expired=0 in 43s, target=14/60s)\n2026/06/21 23:35:33 [vardiff] 192.168.1.14:40389 \u2192 pdiff 0.14038716\n2026/06/21 23:35:33 PUSHDIF -> 192.168.1.14:40389 mining.set_difficulty 0.14038716\n2026/06/21 23:35:34 \u2705 Maturity: marked 2 blocks MATURE currentHeight=9348014 first=0323a4c94f3d3788f77c5b30a7dcecc16b54dc64b784a94a3e25acaa00000000 last=ecae2e801eef6376936013d1da4905c63a2d42559428edea852a62ec00000000 minConfs=13 maxConfs=16\n2026/06/21 23:35:35 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=345-18bb3ccde6ced014_07000000 extranonce2=00000000 ntime=6a387543 nonce=a12e348a suppressed=22\n2026/06/21 23:35:35 [REFRESH] ws tip change to 6fe4a3ca3c286ec72b217fc3920a52105da0c9d5db295eb1d15d16f09f97cbc7 (seq=346 parent=6fe4a3ca3c286ec72b217fc3920a52105da0c9d5db295eb1d15d16f09f97cbc7 suppressed=5)\n2026/06/21 23:35:36 \u2705 valid share accepted 11799.080027 \u2192 773264509 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=347-18bb3ccec1a5c936_07000000 suppressed=15 suppressedDiff=198768.638663 suppressedWork=13026501503\n2026/06/21 23:35:37 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=347-18bb3ccec1a5c936_06000000 extranonce2=00000000 ntime=6a387547 nonce=c98b6047 suppressed=14\n2026/06/21 23:35:37 \ud83c\udfaf BLOCK FOUND height(le)=9348017 job=347-18bb3ccec1a5c936_06000000 hash=0ee5359d532166e4953b6dd11ab12bd50004e6c0d4a06b652c09ae1601000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:37 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=347-18bb3ccec1a5c936_05000000 extranonce2=00000000 ntime=6a387547 nonce=8f27edaa suppressed=13\n2026/06/21 23:35:37 \u2705 Block submitted successfully: map[accepted:true block_hash:0x0d70b57bab558f805dc3fdb2f57d937010666e512f689a67995221099995500f code:accepted coinbase_amount:10 coinbase_txid: height:9.348017e+06 message:accepted miner_type:GBT_Worker order:12207689 template_seq:3843]\n2026/06/21 23:35:37 [REFRESH] accepted block parent advance (seq=347 parent=0d70b57bab558f805dc3fdb2f57d937010666e512f689a67995221099995500f suppressed=1)\n2026/06/21 23:35:37 \ud83d\udcb0 CREDIT SUMMARY block=0ee5359d532166e4953b6dd11ab12bd50004e6c0d4a06b652c09ae1601000000 height=9348017 totalWork=51557195505 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348027 payoutUnlockHeight=9352113 suppressedDetailBlocks=0\n2026/06/21 23:35:37 [REFRESH] ws TTL refresh (seq=348 parent=0d70b57bab558f805dc3fdb2f57d937010666e512f689a67995221099995500f)\n2026/06/21 23:35:37 [REFRESH] block submission accepted (seq=349 parent=0d70b57bab558f805dc3fdb2f57d937010666e512f689a67995221099995500f suppressed=1)\n2026/06/21 23:35:37 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:37 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:38 \u2705 valid share accepted 23645.420279 \u2192 1549626263 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=351-18bb3ccf327f1f15_06000000 suppressed=8 suppressedDiff=110811.015011 suppressedWork=7262110680\n2026/06/21 23:35:39 \ud83c\udfaf BLOCK FOUND height(le)=9348021 job=353-18bb3ccf75fd98e2_06000000 hash=c63c14c8814a93d6da228d78e2291abca37c772863bf364cafc828c700000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:39 \u2705 Block submitted successfully: map[accepted:true block_hash:0x0341fbe9f948289f03944938188099b4df13647f0416ca1d9fc6c8d3f4e799fc code:accepted coinbase_amount:10 coinbase_txid: height:9.348021e+06 message:accepted miner_type:GBT_Worker order:12207694 template_seq:3848]\n2026/06/21 23:35:39 \ud83d\udcb0 CREDIT SUMMARY block=c63c14c8814a93d6da228d78e2291abca37c772863bf364cafc828c700000000 height=9348021 totalWork=51848608113 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348031 payoutUnlockHeight=9352117 suppressedDetailBlocks=0\n2026/06/21 23:35:39 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:39 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:39 [REFRESH] template pressure: stale-job (seq=355 parent=0341fbe9f948289f03944938188099b4df13647f0416ca1d9fc6c8d3f4e799fc)\n2026/06/21 23:35:39 [RECOVERY] resending current job to mac:28:e2:97:1e:c0:b5 addr:192.168.1.101:38825 after 1 stale-template races (job=353-18bb3ccf75fd98e2_06000000 reason=stale-job)\n2026/06/21 23:35:39 [STALE JOB] Block candidate rejected locally worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=353-18bb3ccf75fd98e2_06000000 reason=stale-job backend=node job_age=606ms template_seq=353 current_seq=355 job_parent=f363c058c8a086d0055f48545ea70880c34405597ece92b4fdd205e7ebc29429 current_parent=0341fbe9f948289f03944938188099b4df13647f0416ca1d9fc6c8d3f4e799fc hash=b0bd366513442d7a9b26a9f70569c2cb59157d039488b3e3fc56b34c00000000\n2026/06/21 23:35:39 [REFRESH] stale block candidate: stale-job (seq=356 parent=0341fbe9f948289f03944938188099b4df13647f0416ca1d9fc6c8d3f4e799fc)\n2026/06/21 23:35:39 [REFRESH] invalidated 4 current miner jobs by epoch reason=stale block candidate: stale-job\n2026/06/21 23:35:39 [REFRESH] clean template refresh invalidated_jobs=4 reason=stale block candidate: stale-job\n2026/06/21 23:35:40 [REFRESH] ws tip change to cfe9562293e9c9b3e43d998d5d945e74640176aa6d96149a8bc839d5e0328727 (seq=358 parent=cfe9562293e9c9b3e43d998d5d945e74640176aa6d96149a8bc839d5e0328727 suppressed=6)\n2026/06/21 23:35:43 submit from worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=360-18bb3cd04d21ae56_08000000 extranonce2=00000000 ntime=6a38754d nonce=5a784b47 suppressed=18\n2026/06/21 23:35:44 [ROUTER] preserving current jobs for backend=node during same-parent template invalidation reason=node template invalidation: parent_changed\n2026/06/21 23:35:44 [REFRESH] node template invalidation: parent_changed (seq=361 parent=d0e369d7ec07476d2dc689647210168a90d7cc02936105347eebc614a18f4452)\n2026/06/21 23:35:44 [REFRESH] same-parent template refresh preserved current jobs reason=node template invalidation: parent_changed (seq=361 parent=d0e369d7ec07476d2dc689647210168a90d7cc02936105347eebc614a18f4452)\n2026/06/21 23:35:44 [REFRESH] clean template refresh invalidated_jobs=0 reason=node template invalidation: parent_changed\n2026/06/21 23:35:44 \u2705 Maturity: marked 5 blocks MATURE currentHeight=9348025 first=f22a411ff2ae12659a3d7b5e0752e47786f21d23841d5644d638df1201000000 last=0aa562b858ed928a93db89b1938d78986b0707b8c091e7df923aa43901000000 minConfs=12 maxConfs=19\n2026/06/21 23:35:44 \ud83d\udcb8 Processing Payouts for Block 72c82ef998ad8e2aeaa2b8495d05dac4fd3513fc6b81344da79b83e600000000 (1 credits)\n2026/06/21 23:35:45 \u2705 valid share accepted 12590.569554 \u2192 825135566 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=361-18bb3cd0b9ea409e_05000000 suppressed=14 suppressedDiff=189628.254011 suppressedWork=12427477255\n2026/06/21 23:35:45 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:45 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xeae965ce2430bf0ba2f8acb125ac7f4334f4f4fdeeecd827b0987c59204ad953)\n2026/06/21 23:35:45 \ud83d\udcb0 Block 72c82ef998ad8e2aeaa2b8495d05dac4fd3513fc6b81344da79b83e600000000 marked as PAID\n2026/06/21 23:35:45 \ud83d\udcb8 Processing Payouts for Block 12142eb9bed49fbe2e4eb4aae1c90daa2b46d75b3aeacb96acdafa5202000000 (1 credits)\n2026/06/21 23:35:45 \ud83c\udfaf BLOCK FOUND height(le)=9348026 job=361-18bb3cd0b9ea409e_07000000 hash=05617fd7a3a3a6eb3902b4f2a0c8d4b41dbbacc83bc4cf25541355ef00000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:45 \u2705 Block submitted successfully: map[accepted:true block_hash:0x7dfb052962b589b5f515bd6012772d91a6a5fded50365e7b0150a67577eb6614 code:accepted coinbase_amount:10 coinbase_txid: height:9.348026e+06 message:accepted miner_type:GBT_Worker order:12207699 template_seq:3853]\n2026/06/21 23:35:45 [REFRESH] accepted block parent advance (seq=361 parent=7dfb052962b589b5f515bd6012772d91a6a5fded50365e7b0150a67577eb6614 suppressed=1)\n2026/06/21 23:35:45 [REFRESH] ws TTL refresh (seq=362 parent=7dfb052962b589b5f515bd6012772d91a6a5fded50365e7b0150a67577eb6614 suppressed=1)\n2026/06/21 23:35:45 [REFRESH] block submission accepted (seq=363 parent=7dfb052962b589b5f515bd6012772d91a6a5fded50365e7b0150a67577eb6614 suppressed=1)\n2026/06/21 23:35:45 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:45 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:45 \ud83d\udcb0 CREDIT SUMMARY block=05617fd7a3a3a6eb3902b4f2a0c8d4b41dbbacc83bc4cf25541355ef00000000 height=9348026 totalWork=53556915823 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348036 payoutUnlockHeight=9352122 suppressedDetailBlocks=0\n2026/06/21 23:35:46 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:46 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xa6f9151953168b88321502a7e1a8fb71b1b6dea17d61615317156687cdca00cb)\n2026/06/21 23:35:46 \ud83d\udcb0 Block 12142eb9bed49fbe2e4eb4aae1c90daa2b46d75b3aeacb96acdafa5202000000 marked as PAID\n2026/06/21 23:35:46 \ud83d\udcb8 Processing Payouts for Block 05be13232cfdb981b46603398bae2d7dd8a5c974c9a79f01ab714cf601000000 (1 credits)\n2026/06/21 23:35:46 [REFRESH] ws tip change to ce97ec186cf1f6ca20295600cac97fe7597b729c141b5529966ef1e462ff8321 (seq=364 parent=ce97ec186cf1f6ca20295600cac97fe7597b729c141b5529966ef1e462ff8321 suppressed=2)\n2026/06/21 23:35:47 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:47 [STRATUM] accepted client addr=192.168.1.14:40741 host=192.168.1.14 lane=mac:28:e2:97:3e:39:63 mac=28:e2:97:3e:39:63 suppressed=17\n2026/06/21 23:35:47 [STRATUM] client closed addr=192.168.1.14:40741 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=17\n2026/06/21 23:35:47 \ud83c\udfaf BLOCK FOUND height(le)=9348028 job=364-18bb3cd1382005e8_07000000 hash=3e22158d9b09ec32f1d40c267f0282934eb1d3d685f2cd113e0c03eb00000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:47 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x808bb909118eea3e50e299c67939ac8f88f86ee0a77516c176aa9411b8ec7ff2)\n2026/06/21 23:35:47 \ud83d\udcb0 Block 05be13232cfdb981b46603398bae2d7dd8a5c974c9a79f01ab714cf601000000 marked as PAID\n2026/06/21 23:35:47 \ud83d\udcb8 Processing Payouts for Block 94f59e786ab5413e28449c440466a69654b8535f85c7f987d91b7fb600000000 (1 credits)\n2026/06/21 23:35:47 \u2705 Block submitted successfully: map[accepted:true block_hash:0xc187259871f85392bb0480abc1a0533b249486e0bd5d644e6e0bf042aaeb16d0 code:accepted coinbase_amount:10 coinbase_txid: height:9.348028e+06 message:accepted miner_type:GBT_Worker order:12207701 template_seq:3855]\n2026/06/21 23:35:48 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:48 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:48 \ud83d\udcb0 CREDIT SUMMARY block=3e22158d9b09ec32f1d40c267f0282934eb1d3d685f2cd113e0c03eb00000000 height=9348028 totalWork=53835885623 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348038 payoutUnlockHeight=9352124 suppressedDetailBlocks=0\n2026/06/21 23:35:48 \u2705 valid share accepted 17214.880321 \u2192 1128194397 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=366-18bb3cd17d994f24_08000000 suppressed=15 suppressedDiff=219885.812124 suppressedWork=14410436583\n2026/06/21 23:35:48 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:48 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x37c617a4fadd37d3e05b796ca44cfcb4130d3a6db9d19deb1d5c96e25fcdeb91)\n2026/06/21 23:35:48 \ud83d\udcb0 Block 94f59e786ab5413e28449c440466a69654b8535f85c7f987d91b7fb600000000 marked as PAID\n2026/06/21 23:35:49 \ud83d\udcb8 Processing Payouts for Block 026e735efdd2204e8ff28a61de5d358307dfebfcf8040a30989ca59c01000000 (1 credits)\n2026/06/21 23:35:49 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/21 23:35:50 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xfa007d1de1e7b154ac4cdce7f30ab1581b6110b196cb3297ab9d006c9129ed87)\n2026/06/21 23:35:50 \ud83d\udcb0 Block 026e735efdd2204e8ff28a61de5d358307dfebfcf8040a30989ca59c01000000 marked as PAID\n2026/06/21 23:35:50 [RECOVERY] resending current job to mac:28:e2:97:1e:c0:b5 addr:192.168.1.101:38825 after 1 stale-template races (job=367-18bb3cd1c7c52a4c_06000000 reason=non-current-job)\n2026/06/21 23:35:50 \ud83c\udfaf BLOCK FOUND height(le)=9348031 job=368-18bb3cd1faa85c4e_06000000 hash=4759264d639750e22aae64d2c74e0d822312adc49cfbf87f2503948301000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:51 \u2705 Block submitted successfully: map[accepted:true block_hash:0x40c72c0593c96bdb93532c424c9022c50d8988acee8518d87a8a7cc2134b0430 code:accepted coinbase_amount:10 coinbase_txid: height:9.348031e+06 message:accepted miner_type:GBT_Worker order:12207704 template_seq:3858]\n2026/06/21 23:35:51 [REFRESH] accepted block parent advance (seq=368 parent=40c72c0593c96bdb93532c424c9022c50d8988acee8518d87a8a7cc2134b0430 suppressed=1)\n2026/06/21 23:35:51 [REFRESH] ws TTL refresh (seq=369 parent=40c72c0593c96bdb93532c424c9022c50d8988acee8518d87a8a7cc2134b0430 suppressed=1)\n2026/06/21 23:35:51 [RECOVERY] resending current job to mac:28:e2:97:4d:44:3a addr:192.168.1.105:51188 after 1 stale-template races (job=368-18bb3cd1faa85c4e_05000000 reason=non-current-job)\n2026/06/21 23:35:51 [REFRESH] block submission accepted (seq=370 parent=40c72c0593c96bdb93532c424c9022c50d8988acee8518d87a8a7cc2134b0430 suppressed=1)\n2026/06/21 23:35:51 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:51 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:51 \ud83d\udcb0 CREDIT SUMMARY block=4759264d639750e22aae64d2c74e0d822312adc49cfbf87f2503948301000000 height=9348031 totalWork=55122788725 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348041 payoutUnlockHeight=9352127 suppressedDetailBlocks=0\n2026/06/21 23:35:51 [STRATUM] accepted client addr=192.168.1.102:48327 host=192.168.1.102 lane=mac:2a:71:c7:f5:1f:1e mac=2a:71:c7:f5:1f:1e suppressed=19\n2026/06/21 23:35:51 [STRATUM] client closed addr=192.168.1.102:48327 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=19\n2026/06/21 23:35:51 [STRATUM] accepted client addr=192.168.1.101:39202 host=192.168.1.101 lane=mac:28:e2:97:1e:c0:b5 mac=28:e2:97:1e:c0:b5 suppressed=19\n2026/06/21 23:35:51 [STRATUM] client closed addr=192.168.1.101:39202 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=19\n2026/06/21 23:35:52 \ud83c\udfaf BLOCK FOUND height(le)=9348032 job=370-18bb3cd2425327ee_08000000 hash=467fda1bf0a22c244d9aaf3b15691cb2f9bf56e232f245308309856801000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:52 \ud83c\udfaf BLOCK FOUND height(le)=9348032 job=370-18bb3cd2425327ee_08000000 hash=ed2f2baa44525c78f1a50ac2c3c834e430f2dbca8badf2abfb22667301000000 target=02301a0000000000000000000000000000000000000000000000000000\n2026/06/21 23:35:52 [REFRESH] ws tip change to 1f1bfd9f10a32548f3dd857d908093d605cb00f213547e4275f5a93fb6b934be (seq=371 parent=1f1bfd9f10a32548f3dd857d908093d605cb00f213547e4275f5a93fb6b934be suppressed=2)\n2026/06/21 23:35:52 \u2705 Block submitted successfully: map[accepted:true block_hash:0xc7e139710c1af690036ea0a5b4755b6d475252af3782f8ca955154a2a50396ac code:accepted coinbase_amount:10 coinbase_txid: height:9.348032e+06 message:accepted miner_type:GBT_Worker order:uncertainty template_seq:3860]\n2026/06/21 23:35:52 [REFRESH] accepted block parent advance skipped: current parent already superseded (seq=371 parent=1f1bfd9f10a32548f3dd857d908093d605cb00f213547e4275f5a93fb6b934be suppressed=1)\n2026/06/21 23:35:52 \ud83d\udcb0 CREDIT SUMMARY block=467fda1bf0a22c244d9aaf3b15691cb2f9bf56e232f245308309856801000000 height=9348032 totalWork=55543950211 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348042 payoutUnlockHeight=9352128 suppressedDetailBlocks=0\n2026/06/21 23:35:52 \u2705 Block submitted successfully: map[accepted:true block_hash:0x2d1777f39bf4428be5e8fbd20731b714f2d457ddc5736b41d457fd89d3aca818 code:accepted coinbase_amount:10 coinbase_txid: height:9.348032e+06 message:accepted miner_type:GBT_Worker order:uncertainty template_seq:3860]\n2026/06/21 23:35:52 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/21 23:35:52 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/21 23:35:52 \ud83d\udcb0 CREDIT SUMMARY block=ed2f2baa44525c78f1a50ac2c3c834e430f2dbca8badf2abfb22667301000000 height=9348032 totalWork=55543950211 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9348042 payoutUnlockHeight=9352128 suppressedDetailBlocks=0", + "ports": "" + }, + "status-sampler": { + "image_latest_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/bin/sh", + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Env": [ + "BDAG_STATUS_SAMPLER_FILE=/home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json", + "BDAG_MINING_IMPERATIVE_GUARD_UNITS=", + "BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS=30", + "BDAG_CHAIN_STATE_SELF_HEAL_COMMAND=ops/chain-state-self-heal.sh --from-systemd", + "NODE_RPC_PASS=", + "BDAG_CHAIN_STATE_SELF_HEAL_UNIT=", + "BDAG_NODE_RPC_URLS=node=http://host.docker.internal:38131", + "BDAG_POOL_DB_CONTAINER=postgres", + "BDAG_NODE_SERVICES=node", + "BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60", + "BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL=http://dashboard:8088/api/status", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_POOL_CONTAINERS=pool", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "TMPDIR=/tmp", + "BDAG_POOL_CONTAINER=pool", + "COMPOSE_PROJECT_NAME=stack", + "BDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10", + "NODE_RPC_USER=test", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "Hostname": "dc4efa9d3462", + "Image": "stack-status-sampler", + "Labels": { + "com.docker.compose.config-hash": "9d60a4b5400ef9e3431e282622311aaaff140c76375cfffe3ac6613c28f1e091", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "status-sampler", + "com.docker.compose.service": "status-sampler", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Status Sampler" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "1000:1000", + "Volumes": null, + "WorkingDir": "/home/jeremy/blockdag-source/stack-redis" + }, + "Created": "2026-06-21T21:55:34.980730129Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/var/run/docker.sock:/var/run/docker.sock:rw", + "/home/jeremy/blockdag-source/stack-redis:/home/jeremy/blockdag-source/stack-redis:rw" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 100, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": [ + "115" + ], + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hostname", + "HostsPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hosts", + "Id": "dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757", + "Image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "ImageManifestDescriptor": { + "digest": "sha256:14ad5cf6cca8e3a1be3b533311cc93dc62783a0f8190e9edca3b760a3245c6af", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2572 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/var/run/docker.sock", + "Type": "bind" + }, + { + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Type": "bind" + } + ], + "Name": "/status-sampler", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "status-sampler", + "status-sampler" + ], + "DNSNames": [ + "status-sampler", + "dc4efa9d3462" + ], + "DriverOpts": null, + "EndpointID": "bea9145ad37eec88c0035936c500122a15538bbae3d48da30f659295fb0c2fca", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "4a:58:5e:1c:0a:0e", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": {}, + "SandboxID": "ac7a75b21d14e791247df23bd6b200be7bb07adfe71b74c3c2a21329237a4909", + "SandboxKey": "" + }, + "Path": "/bin/sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3470155, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T21:55:45.539854201Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "", + "ports": "" + } + }, + "created_at_sast": "2026-06-22T01:35:53+0200", + "dashboard_healthz": { + "global_age_seconds": 4, + "global_chain_latest_block": "11779555", + "global_latest_block": "11779555", + "global_status": "ok", + "global_stream_lag_blocks": "0", + "redis": true, + "status": "ok" + }, + "dashboard_live_status": { + "api_error": "", + "asic_rows": [ + { + "blocks_1h": "30", + "blocks_24h": "30", + "blocks_30m": "30", + "health": "ok", + "ip": "192.168.1.101", + "job_age": "954 ms", + "last_block": "Jun 22, 2026 01:35:39 SAST", + "mac": "28:e2:97:1e:c0:b5", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "3" + }, + { + "blocks_1h": "22", + "blocks_24h": "22", + "blocks_30m": "22", + "health": "ok", + "ip": "192.168.1.102", + "job_age": "954 ms", + "last_block": "Jun 22, 2026 01:35:47 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "0" + }, + { + "blocks_1h": "28", + "blocks_24h": "28", + "blocks_30m": "28", + "health": "ok", + "ip": "192.168.1.105", + "job_age": "954 ms", + "last_block": "Jun 22, 2026 01:35:10 SAST", + "mac": "28:e2:97:4d:44:3a", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "1" + }, + { + "blocks_1h": "26", + "blocks_24h": "26", + "blocks_30m": "26", + "health": "ok", + "ip": "192.168.1.14", + "job_age": "954 ms", + "last_block": "Jun 22, 2026 01:35:15 SAST", + "mac": "28:e2:97:3e:39:63", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "0" + } + ], + "chart_json": "[{\"color\":\"#2bd4a3\",\"label\":\"28:e2:97:1e:c0:b5\",\"mac\":\"28:e2:97:1e:c0:b5\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":30,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-21T23:30:00Z\"}],\"ready\":true},{\"color\":\"#f5c542\",\"label\":\"28:e2:97:3e:39:63\",\"mac\":\"28:e2:97:3e:39:63\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":26,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:30:00Z\"}],\"ready\":true},{\"color\":\"#ff7a7a\",\"label\":\"28:e2:97:4d:44:3a\",\"mac\":\"28:e2:97:4d:44:3a\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":28,\"rejected\":0,\"rejected_local\":1,\"t\":\"2026-06-21T23:30:00Z\"}],\"ready\":true},{\"color\":\"#7ca7ff\",\"label\":\"2a:71:c7:f5:1f:1e\",\"mac\":\"2a:71:c7:f5:1f:1e\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-20T23:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T00:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T01:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T02:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T03:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":22,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:30:00Z\"}],\"ready\":true}]", + "evidence": [ + { + "label": "Live Mode", + "value": "mining" + }, + { + "label": "Live Overall", + "value": "ok" + }, + { + "label": "Miner Activity", + "value": "yes" + }, + { + "label": "Accepting Shares", + "value": "yes" + }, + { + "label": "Submit Ready", + "value": "yes" + }, + { + "label": "Getting Fresh Work", + "value": "yes" + }, + { + "label": "Won a block in last 600?", + "value": "yes" + } + ], + "groups": null, + "node": { + "evidence": [ + { + "label": "Node Block Height", + "value": "12,207,706" + }, + { + "label": "Peer Main Order", + "value": "12,207,705" + }, + { + "label": "Peer Order Gap", + "value": "0" + }, + { + "label": "Peers", + "value": "17" + }, + { + "label": "EVM Block Height", + "value": "11,840,497" + } + ], + "state": "Synced", + "status_steps": [ + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "P2P Down" + }, + { + "active": false, + "label": "Syncing" + }, + { + "active": true, + "label": "Synced" + }, + { + "active": false, + "label": "Node Degraded" + } + ], + "title": "Node Status", + "tone": "ok" + }, + "notes": null, + "refresh_every": "5s", + "rows": [ + { + "identity": "28:e2:97:1e:c0:b5", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.101", + "last": "Jun 22, 2026 01:35:43 SAST", + "mac": "28:e2:97:1e:c0:b5", + "observed": "192.168.1.101", + "shares": "0", + "status": "ok" + }, + { + "identity": "2a:71:c7:f5:1f:1e", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.102", + "last": "Jun 22, 2026 01:35:43 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "observed": "192.168.1.102", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:4d:44:3a", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.105", + "last": "Jun 22, 2026 01:35:43 SAST", + "mac": "28:e2:97:4d:44:3a", + "observed": "192.168.1.105", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:3e:39:63", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.14", + "last": "Jun 22, 2026 01:35:43 SAST", + "mac": "28:e2:97:3e:39:63", + "observed": "192.168.1.14", + "shares": "0", + "status": "ok" + } + ], + "secondary": [ + { + "label": "Miner Inventory", + "value": "4" + }, + { + "label": "Valid Shares", + "value": "504" + }, + { + "label": "Last Share Age", + "value": "not known" + }, + { + "label": "Submit Errors", + "value": "0" + }, + { + "label": "Template Coinbase Valid", + "value": "unknown" + }, + { + "label": "Zero Coinbase Reject Rate", + "value": "unknown" + }, + { + "label": "Ready Miner Jobs", + "value": "4" + }, + { + "label": "Local Stale Ratio", + "value": "unknown" + }, + { + "label": "Last Automation Restart", + "value": "none" + }, + { + "label": "Pool Endpoint", + "value": "192.168.1.100:3334" + } + ], + "state": "Mining", + "state_tone": "ok", + "status_steps": [ + { + "active": false, + "label": "Waiting for Live Data" + }, + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "Waiting for a Miner" + }, + { + "active": false, + "label": "Waiting for Node Sync" + }, + { + "active": false, + "label": "Template Unsafe" + }, + { + "active": false, + "label": "Degraded" + }, + { + "active": true, + "label": "Mining" + } + ], + "title": "Pool Status", + "updated_at": "Jun 22, 2026 01:35:53 SAST", + "warnings": null + }, + "dashboard_status": { + "asic_performance": { + "age_seconds": 5, + "bucket_seconds": 600, + "generated_at_unix_ms": 1782084948962, + "lanes": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "blocks_1h": 30, + "blocks_24h": 30, + "blocks_30m": 30, + "blocks_6h": 30, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 30, + "rejected": 0, + "rejected_local": 3, + "total": 33 + }, + "start_unix_ms": 1782084600000 + } + ], + "client_addr": "192.168.1.101:38825", + "current_job_age_ms": 954, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084939793, + "last_event_unix_ms": 1782084939896, + "pdiff": 0.1457063045063559, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 3, + "remote_host": "192.168.1.101", + "totals": { + "accepted": 30, + "rejected": 0, + "rejected_local": 3, + "total": 33 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "blocks_1h": 26, + "blocks_24h": 26, + "blocks_30m": 26, + "blocks_6h": 26, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 26, + "rejected": 0, + "rejected_local": 0, + "total": 26 + }, + "start_unix_ms": 1782084600000 + } + ], + "client_addr": "192.168.1.14:40389", + "current_job_age_ms": 954, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:3e:39:63", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084915521, + "last_event_unix_ms": 1782084915521, + "pdiff": 0.14038716275222374, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 0, + "remote_host": "192.168.1.14", + "totals": { + "accepted": 26, + "rejected": 0, + "rejected_local": 0, + "total": 26 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "blocks_1h": 28, + "blocks_24h": 28, + "blocks_30m": 28, + "blocks_6h": 28, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 28, + "rejected": 0, + "rejected_local": 1, + "total": 29 + }, + "start_unix_ms": 1782084600000 + } + ], + "client_addr": "192.168.1.105:51188", + "current_job_age_ms": 954, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:4d:44:3a", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084910905, + "last_event_unix_ms": 1782084910905, + "pdiff": 0.1441177717437917, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 1, + "remote_host": "192.168.1.105", + "totals": { + "accepted": 28, + "rejected": 0, + "rejected_local": 1, + "total": 29 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "blocks_1h": 22, + "blocks_24h": 22, + "blocks_30m": 22, + "blocks_6h": 22, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781998800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1781999400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782000600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782001800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782002400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782003600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782004800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782005400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782006600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782007800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782008400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782009600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782010800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782011400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782012600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782013800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782014400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 22, + "rejected": 0, + "rejected_local": 0, + "total": 22 + }, + "start_unix_ms": 1782084600000 + } + ], + "client_addr": "192.168.1.102:47956", + "current_job_age_ms": 954, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782084947651, + "last_event_unix_ms": 1782084947651, + "pdiff": 0.1385826388247724, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 0, + "remote_host": "192.168.1.102", + "totals": { + "accepted": 22, + "rejected": 0, + "rejected_local": 0, + "total": 22 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + } + ], + "last_update_age_seconds": 5, + "source": "pool-health-asic-performance", + "updated_at": "2026-06-21T23:35:48Z", + "updated_at_epoch": 1782084948, + "window_seconds": 86400 + }, + "can_accept_shares": true, + "can_submit_blocks": true, + "chain_block_count": "12207706", + "containers": { + "node": { + "running": true + }, + "pool": { + "running": true + } + }, + "miner_health": { + "configured_count": 4, + "connected_count": 4, + "connected_count_effective": 4, + "identity_contract": "mac-only", + "managed_count": 4, + "managed_miners": [ + { + "blocks_1h": "30", + "blocks_24h": "30", + "blocks_30m": "30", + "blocks_6h": "30", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782084939793", + "last_event_at_epoch_ms": "1782084939896", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.1457063045063559", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "3", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "22", + "blocks_24h": "22", + "blocks_30m": "22", + "blocks_6h": "22", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782084947651", + "last_event_at_epoch_ms": "1782084947651", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.1385826388247724", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "0", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "28", + "blocks_24h": "28", + "blocks_30m": "28", + "blocks_6h": "28", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782084910905", + "last_event_at_epoch_ms": "1782084910905", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.1441177717437917", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "1", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "26", + "blocks_24h": "26", + "blocks_30m": "26", + "blocks_6h": "26", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782084915521", + "last_event_at_epoch_ms": "1782084915521", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.14038716275222374", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "0", + "source": "env-overrides", + "status": "ok" + } + ], + "miners": [ + { + "blocks_1h": "30", + "blocks_24h": "30", + "blocks_30m": "30", + "blocks_6h": "30", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782084939793", + "last_event_at_epoch_ms": "1782084939896", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.1457063045063559", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "3", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "22", + "blocks_24h": "22", + "blocks_30m": "22", + "blocks_6h": "22", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782084947651", + "last_event_at_epoch_ms": "1782084947651", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.1385826388247724", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "0", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "28", + "blocks_24h": "28", + "blocks_30m": "28", + "blocks_6h": "28", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782084910905", + "last_event_at_epoch_ms": "1782084910905", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.1441177717437917", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "1", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "26", + "blocks_24h": "26", + "blocks_30m": "26", + "blocks_6h": "26", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "954", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782084915521", + "last_event_at_epoch_ms": "1782084915521", + "last_seen_at": "2026-06-21T23:35:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.14038716275222374", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "0", + "source": "env-overrides", + "status": "ok" + } + ], + "pool_ready_overlay": true, + "source": "redis-asic-status", + "source_key": "", + "tracked_count": 4 + }, + "mode": "mining", + "overall": "ok", + "pool_health": { + "accepted_block_count": "106", + "block_submit_error_count": "0", + "block_submit_success_count": "106", + "source_job_health": { + "authorized_miners": "4", + "ready_miners": "4" + }, + "stale_jobs": false, + "valid_share_count": "504" + }, + "pool_metrics": { + "accepted_block_recent": true, + "accepted_block_submissions": 106, + "accepted_block_submissions_delta": 2, + "accepted_block_submissions_rate_per_second": 0.4, + "active_connections": 4, + "authorized_miners": 4, + "backend_main_order": 12207698, + "backend_mineable": 1, + "backend_p2p_mining_fresh": 1, + "backend_peer_lead_blocks": 0, + "backend_submit_ready": 1, + "blocks_found_total": 106, + "expired_miners": 0, + "invalidated_miners": 0, + "last_accepted_block_submission_age_seconds": 0, + "last_accepted_block_submission_at_epoch": 1782084948, + "ready_miners": 4, + "rejected_block_submissions": 0, + "shares_accepted_total": 504, + "shares_rejected_total": 20, + "stale_miners": 0, + "template_age_seconds": 1.829 + }, + "source": "redis-live", + "status": "ok", + "sync_progress": { + "best_peer_mainorder": 12207705, + "chain_block_count": 12207706, + "chain_rpc_source": "getBlockCount", + "chain_syncing": false, + "current_block": 12207706, + "eth_syncing": { + "currentBlock": "0xb4abf1", + "highestBlock": "0xb4f228", + "startingBlock": "0x0" + }, + "evm_block_count": 11840497, + "evm_chain_syncing": true, + "evm_rpc_url": "http://host.docker.internal:18545", + "evm_sync_advisory": "eth_syncing active while native P2P mining state is current", + "highest_block": 12207706, + "mining_advisory_sync": true, + "native_is_current": true, + "p2p_connections": 17, + "p2p_network_gap": 0, + "p2p_network_height": 12207705, + "peer_count": 17, + "peer_mainorder_gap": 0, + "remaining_blocks": 0, + "source": "native-rpc", + "status": "synced", + "sync_current_block": 11840497, + "sync_highest_block": 11858472 + }, + "updated_at": "2026-06-21T23:35:53Z" + }, + "docker_ps": "NAMES STATUS IMAGE\npool Up 4 minutes stack-pool\ndashboard Up 6 minutes (healthy) stack-dashboard\nnode Up About an hour stack-node\nstatus-sampler Up 2 hours stack-status-sampler\npostgres Up 4 hours (healthy) postgres:15-bookworm", + "docker_stats": "NAME CPU % MEM USAGE / LIMIT BLOCK I/O NET I/O\nnode 25.70% 2.13GiB / 15.31GiB 273MB / 133GB 0B / 0B\npool 2.82% 19MiB / 15.31GiB 0B / 0B 0B / 0B\ndashboard 2.64% 30.53MiB / 15.31GiB 68.1MB / 25.6MB 13MB / 3.03MB\nstatus-sampler 24.52% 28.28MiB / 15.31GiB 32.9MB / 46.2MB 71.7MB / 10.7MB", + "env_file": "COMPOSE_PROJECT_NAME=stack\nDOCKERFILE=dockerfile-dev\nDOCKER_PLATFORM=linux/amd64\n# Optional overrides for dockerfile-dev named build contexts.\"\n# BLOCKDAG_CORECHAIN_CONTEXT=../blockdag-corechain\n# POOL_SRC_CONTEXT=../pool\n# CPU_MINER_SRC_CONTEXT=../cpu-miner\nCOLLECTOR_SRC_CONTEXT=/home/jeremy/blockdag-source/collector\n# DASHBOARD_SRC_CONTEXT=../dashboard2\"\n# Paths are relative to tarball root (same directory as docker-compose.yml).\n# To import a snapshot during build, place latest.bdsnap in the payload root and\n# set SNAPSHOT_PATH=./latest.bdsnap. The default marker means \"build without a\n# baked-in snapshot.\"\nSNAPSHOT_PATH=docker/no-snapshot.marker\n# SNAPSHOT_PATH=docker/latest.bdsnap\"\n\n# Snapshot download link for this node. The installer writes this based on the\n# selected install mode. Naming convention on the snapshot host:\n# latest.bdsnap -> non-archive (pruned) snapshot\n# latest-archive.bdsnap -> archive (full history) snapshot\n# The node entrypoint downloads from this URL at first start when no local\n# snapshot or chain data exists.\nBDAG_SNAPSHOT_URL=\n# Set to 1 to run the node in archival mode (--archival; consensus keeps all\n# blocks instead of pruning). Archive nodes should bootstrap from the archive\n# snapshot so historical data is actually present.\nBDAG_NODE_ARCHIVAL=0\n# Optional compose services:\n# blank -> base stack only\n# miner -> include CPU miner service (requires DOCKERFILE=dockerfile-dev)\n# COMPOSE_PROFILES=miner\n\n# Dashboard repository. Release builds always use the main branch.\nDASHBOARD_REPO=https://github.com/BlockdagEngineering/redis-dash.git\nP2P_PORT=8155\nBDAG_RPC_PORT=38131\nEVM_HTTP_PORT=18545\nEVM_WS_PORT=18546\nWALLET_RPC_URL=http://127.0.0.1:18545\nNODE_METRICS_PORT=6060\nPOOL_BIND_PORT=3334\nPOOL_API_PORT=9090\nCOLLECTOR_HOST_PORT=9280\nDASHBOARD_HOST_PORT=8088\n# Dashboard UI display timezone.\nDASHBOARD_TIMEZONE=Africa/Johannesburg\n# NODE_DATA_DIR=./node-data\n\n# Required for any pool/miner deployment. Do not use the zero address.\n# POOL_COINBASE_ADDRESS may be set separately when it must differ from the\n# displayed miner/pool identity; otherwise set MINING_POOL_ADDRESS only.\nMINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\n# Storage placement for constrained mining appliances. Release installers keep\n# growing node chain data on capacity storage, then move frequent small writes\n# such as Postgres and dashboard/runtime state to internal storage when it has\n# enough free space.\nBDAG_STORAGE_PROFILE=auto\nBDAG_CHAIN_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_NODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nBDAG_POSTGRES_DATA_DIR=./data/postgres\nBDAG_RUNTIME_DIR=./ops/runtime\nBDAG_STORAGE_MIN_CHAIN_FREE_GIB=50\nBDAG_STORAGE_MIN_RUNTIME_FREE_GIB=4\n\n# Ephemeral scratch policy. Small temporary files belong on RAM-backed tmpfs so\n# they do not add avoidable writes to the USB chain device. Large snapshot and\n# chain staging must stay on capacity storage unless explicitly sized.\nBDAG_EPHEMERAL_TMPFS_ENABLED=1\nBDAG_EPHEMERAL_DIR=/run/bdag-pool\nBDAG_HOST_TMPDIR=/run/bdag-pool/tmp\nBDAG_CONTAINER_TMPFS_SIZE=128m\nBDAG_NODE_TMPFS_SIZE=512m\nBDAG_NODE_SHM_SIZE=512m\nBDAG_BUILD_TMPDIR=./.build-tmp\nBDAG_BUILD_CACHE_WARN_GIB=4\nBDAG_ROOT_FREE_WARN_GIB=6\nBDAG_ROOT_FREE_FAIL_GIB=2\n\n# Single P2P host port used by Docker publishing, firewall, and local peer\n# advertisement.\nBDAG_P2P_PROTOCOLS=tcp\n# Standard mining-host performance defaults. The release topology has one\n# production node with a direct pool-to-node RPC path.\nBDAG_NODE_CACHE_MB=6144\nBDAG_EVM_CACHE_MB=6144\nBDAG_NODE_CACHE_DATABASE_PERCENT=70\nBDAG_NODE_OBSOLETE_HEIGHT=20\nBDAG_NODE_MINING_NO_PENDING_TX=0\nBDAG_BLOCK_READ_AHEAD_KB=1024\nBDAG_BLOCK_NR_REQUESTS=256\nBDAG_NODE_CPU_SHARES=6144\nBDAG_POOL_CPU_SHARES=5120\nBDAG_POOL_DB_CPU_SHARES=4096\nBDAG_DASHBOARD_CPU_SHARES=128\nBDAG_NODE_MEMORY_LOW=768M\nBDAG_POOL_MEMORY_LOW=256M\nBDAG_POOL_DB_MEMORY_LOW=512M\nBDAG_DASHBOARD_MEMORY_LOW=64M\nBDAG_MINING_ACTIVE_NODE_NICE=-10\nBDAG_MINING_POOL_NICE=-8\nBDAG_OBSERVABILITY_NICE=15\nBDAG_TUNE_NET_QDISC=1\nBDAG_STACK_NET_IFACE=\nPOOL_RPC_BACKENDS=\n\n# Release install support services. Fresh appliance installs apply the\n# non-destructive host profile by default, then install P2P/IPFS support timers\n# and the mining-host tuning timer. Set the strict toggles to fail the install\n# instead of warning when host-level service installation cannot complete.\nBDAG_INSTALL_APPLIANCE_HOST_PROFILE=1\nBDAG_INSTALL_APPLIANCE_PROFILE_DISABLE_SERVICES=0\nBDAG_INSTALL_APPLIANCE_PROFILE_RELOAD_DOCKER=1\nBDAG_INSTALL_APPLIANCE_PROFILE_STRICT=0\nBDAG_INSTALL_STACK_SUPPORT_SERVICES=1\nBDAG_INSTALL_STACK_SUPPORT_SERVICES_STRICT=0\n\n# Enables localhost/private-network pool runtime admin endpoints. Keep the pool\n# metrics/admin port bound to localhost on public hosts.\nPOOL_RUNTIME_ADMIN_ENABLED=false\nMETRICS_ADDR=0.0.0.0:9090\nPOOL_GBT_MIN_INTERVAL_MS=1100\nPOOL_GBT_PRESSURE_INTERVAL_MS=500\nPOOL_GBT_PRESSURE_WINDOW_SECONDS=10\nPOOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15\nPOOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30\n# Host network discovery is only for choosing this node's advertised P2P address.\n# Peer candidates should be complete P2P multiaddrs.\nBDAG_NETWORK_TOPOLOGY=auto\nBDAG_DETECTED_NETWORK_TOPOLOGY=\nBDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\nBDAG_ASIC_LAN_CIDRS=192.168.1.0/24\n# Optional route to miners on a remote LAN reached through a routed gateway\n# (e.g. a ZeroTier peer). When set, the miner-route compose service keeps the\n# host route alive so the watchdog can reach the miner HTTP API. Leave empty\n# when miners are on the host LAN.\nBDAG_MINER_ROUTE_SUBNET=\nBDAG_MINER_ROUTE_GATEWAY=\nBDAG_MINER_ROUTE_DEV=\n# Docker bridge/container networks must not be treated as ASIC LANs. Override\"\n# only if a real ASIC LAN intentionally uses these ranges.\nBDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12\nBDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0\nBDAG_P2P_ADVERTISE_IP=\nBDAG_P2P_INTERFACE=\nBDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1\nBDAG_CHAIN_PEERSTORE_LOG_TAIL=8000\nEXTRA_PEER_ADDRESSES=\n# When enabled by installer/watchdog for attached ASICs, this must include\n# --miner, --miningaddr=, and --maxinbound=1 on constrained\n# USB/router hosts. Do not add --allowminingwhennearlysynced or\n# --allowsubmitwhennotsynced; template/readiness health must fail closed when\n# P2P freshness or sync safety is unavailable.\nBDAG_NODE_MINING_ARGS=\nBDAG_NODE_CHAIN_RPC_TIMEOUT=8.0\nBDAG_NODE_CHAIN_RPC_RETRIES=2\nBDAG_POOL_RPC_REFUSED_WARN_SECONDS=120\nBDAG_HOST_PRESSURE_IOWAIT_WARN_PERCENT=25\nBDAG_HOST_PRESSURE_IOWAIT_WARN_SAMPLES=3\nBDAG_HOST_PRESSURE_HISTORY_SAMPLES=6\nBDAG_SHARED_STATUS_CACHE_ENABLED=1\nBDAG_SHARED_STATUS_CACHE_SECONDS=3.0\nBDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS=120\nBDAG_HOST_PROFILE=auto\nBDAG_ADAPTIVE_CONCURRENCY_ENABLED=1\nBDAG_ADAPTIVE_IOWAIT_WARN_PERCENT=25\nBDAG_ADAPTIVE_IO_SOME_AVG10_WARN=20.0\nBDAG_ADAPTIVE_CPU_SOME_AVG10_WARN=80.0\nBDAG_ADAPTIVE_CHAIN_RPC_WARN_MS=1000\nBDAG_STATUS_SAMPLER_ENABLED=1\nBDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10\nBDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120\nBDAG_COLLECTOR_DIRECT_STATUS_FALLBACK=0\nBDAG_COLLECTOR_STATUS_CACHE_SECONDS=10\nBDAG_COLLECTOR_SAMPLER_CACHE_SECONDS=120\nBDAG_COLLECTOR_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_CHAIN_STATE_SELF_HEAL_ENABLED=1\nBDAG_CHAIN_STATE_SELF_HEAL_UNIT=bdag-chain-state-self-heal.service\nBDAG_MINING_IMPERATIVE_CHAIN_STATE_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_SECONDS=900\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS=1000\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_GAP_GROWTH_BLOCKS=60\nBDAG_CHAIN_STATE_REUSE_EXISTING_SNAPSHOT=1\n# Optional trusted restore source for unattended chain-state self-heal.\"\n# Use key-based SSH for remote rsync sources; do not store passwords here.\nBDAG_CHAIN_STATE_RESTORE_SOURCE=\nBDAG_CHAIN_STATE_RESTORE_SNAPSHOT=\nBDAG_CHAIN_STATE_RESTORE_SSH_COMMAND=\nBDAG_DASHBOARD_DIRECT_STATUS_FALLBACK=0\nBDAG_DASHBOARD_STATUS_CACHE_SECONDS=10\nBDAG_DASHBOARD_SAMPLER_CACHE_SECONDS=120\nBDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_EARNINGS_SAMPLER_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_GLOBAL_SAMPLER_INTERVAL_SECONDS=60\nBDAG_OPS_UID=1000\nBDAG_OPS_GID=1000\nBDAG_DOCKER_SOCKET_GID=115\nBDAG_GLOBAL_CACHE_TTL_SECONDS=60\nBDAG_GLOBAL_RPC_WORKERS=4\nBDAG_GLOBAL_BLOCK_WINDOW=600\nBDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOTS=1\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_HOURS=720\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WINDOW_BLOCKS=64\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WORKERS=12\nBDAG_DASHBOARD_HISTORY_REBUILD_PRESERVE_ASIC_HISTORY=1\n# Optional native mining RPC list for Global chain-production stats.\"\n# EVM RPC variables are wallet/balance sources only and must not drive Global mining attribution.\nBDAG_GLOBAL_CHAIN_RPC_URLS=\nBDAG_MINER_SCAN_WORKERS=64\nBDAG_MINER_HASHRATE_PROBE_WORKERS=8\nBDAG_BACKGROUND_MAINTENANCE_BACKOFF_ENABLED=1\nBDAG_BACKGROUND_MAINTENANCE_SYNC_BACKOFF_BLOCKS=0\nBDAG_BACKGROUND_MAINTENANCE_IOWAIT_WARN_PERCENT=25\nBDAG_BACKGROUND_MAINTENANCE_IO_SOME_AVG10_WARN=20.0\nBDAG_BACKGROUND_MAINTENANCE_CPU_SOME_AVG10_WARN=80.0\nBDAG_BACKGROUND_MAINTENANCE_CHAIN_RPC_WARN_MS=1000\nBDAG_BACKGROUND_MAINTENANCE_POOL_READY_TASKS=rawdatadir_publish,ipfs_content_sidecar,ipfs_segment_writer\nBDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=2\nBDAG_ENTRYPOINT_CHOWN_MODE=needed\nBDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\n# Current Compose/service-name topology. Source Compose intentionally does not\n# pin container_name; Docker Compose may append project and ordinal suffixes to\n# concrete container names.\nBDAG_POOL_CONTAINER=pool\nBDAG_POOL_CONTAINERS=pool\nBDAG_POOL_DB_CONTAINER=postgres\nBDAG_NODE_SERVICES=node\nBDAG_STACK_SERVICES=postgres,node,pool\nBDAG_NODE_RPC_URLS=node=http://127.0.0.1:38131\n\n# Optional pre-start P2P snapshot bootstrap. When the node datadir is empty,\n# the node image uses ordinary trusted/addpeer candidates for FastSnap V2 first,\n# imports the verified snapshot, then normal FastSync catches the remaining tail.\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=90s\nBDAG_FASTSNAP_ARTIFACT_V2=1\n# Prefer Fast Artifact Sync V2 directory artifacts when a peer offers them.\n# This avoids building/importing a temporary .bdsnap archive. The archive\n# snapshot RPC path is used only when the serving peer lacks directory support.\nBDAG_FASTSNAP_DIRECTORY_MODE=1\nBDAG_FASTSNAP_DIRECTORY_STAGING=\nBDAG_FASTSNAP_DIRECTORY_REPLACE_EXISTING=1\nBDAG_FASTSNAP_DIRECTORY_MOVE_STAGING=1\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_PARALLELISM=4\nBDAG_FASTSNAP_LEDGER=\n# Archive FastSnap seed serving is deprecated for release installs.\n# The raw datadir FastArtifact source sidecar below is the preferred sync-source\n# path. Keep this disabled unless an offline export workflow is being\n# investigated away from the live mining host.\nBDAG_FASTSNAP_SEED_TIMER_ENABLED=0\n# Raw datadir FastArtifact source sidecar. Installer auto-enables the timer only\n# when chain/stage/artifact paths are not USB/removable and the host has enough\n# CPU, RAM, and disk headroom. Publishes require an explicit operator-approved\n# finalization window.\nSYNC_SOURCE_NODE=0\nBDAG_RAWDATADIR_ARTIFACT_BASE=./data-restore/rawdatadir\nBDAG_RAWDATADIR_ARTIFACT_KEEP=3\nBDAG_RAWDATADIR_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_REQUIRE_EVM_REFERENCE_FRESH=1\nBDAG_RAWDATADIR_MAX_EVM_REFERENCE_LAG=1000\nBDAG_PUBLIC_RPC_URLS=bdagscan-rpc=https://rpc.bdagscan.com,blockdag-engineering-rpc=https://rpc.blockdag.engineering\nBDAG_RAWDATADIR_SIDECAR_SOURCE=\nBDAG_RAWDATADIR_SIDECAR_DIR=\nBDAG_RAWDATADIR_SIDECAR_CONTENT_MODE=auto\nBDAG_RAWDATADIR_SIDECAR_CONTENT_BASE=./data-restore/rawdatadir-sidecar-content\nBDAG_RAWDATADIR_SIDECAR_CONTENT_KEEP=2\nBDAG_RAWDATADIR_SIDECAR_CONTENT_CHUNK_SIZE=67108864\nBDAG_RAWDATADIR_SIDECAR_CONTENT_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_SIDECAR_CONTENT_ALLOW_HOT_PUBLISH=0\nBDAG_RAWDATADIR_OPEN_SIDECAR_ENABLED=1\nBDAG_RAWDATADIR_OPEN_SIDECAR_BASE=./data-restore/rawdatadir-sidecar-open/mainnet\nBDAG_RAWDATADIR_OPEN_SIDECAR_KEEP=12\nBDAG_RAWDATADIR_SIDECAR_RSYNC_BWLIMIT=4096\nBDAG_RAWDATADIR_SIDECAR_DELAY_UPDATES=0\nBDAG_RAWDATADIR_ACTIVE_SERVICE=node\nBDAG_RAWDATADIR_FINALIZE=0\nBDAG_RAWDATADIR_MIN_FREE_GIB=100\nBDAG_RAWDATADIR_FREE_SPACE_MULTIPLIER=2.5\nBDAG_RAWDATADIR_REQUIRE_STATE_ROOT=1\nBDAG_RAWDATADIR_SIDECAR_USE_SUDO=auto\nBDAG_RAWDATADIR_ARCHIVE_USE_SUDO=auto\nBDAG_RAWDATADIR_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_RAWDATADIR_TRUSTED_SIGNERS=\n# Optional Fast Artifact Sync V2 directory hot-stage serving. Set these only on\n# nodes that maintain a verified directory artifact mirror. The manifest path is\n# allowed to be a sidecar outside the directory to avoid copying control files\n# into a future install target.\nBDAG_FASTSYNC_ARTIFACT_DIRECTORY=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST=\n\n# Lazy IPFS content sidecar. The rawdatadir sidecar seals immutable signed\n# file/chunk generations into BDAG_RAWDATADIR_SIDECAR_CONTENT_BASE; IPFS pins\n# only publishable generations after mining and host resource gates pass. IPFS\n# is byte transport; signed manifests and consensus validation remain\n# authoritative.\nBDAG_IPFS_CONTENT_SIDECAR_MODE=auto\nBDAG_IPFS_CONTENT_ARTIFACT_DIR=./data-restore/rawdatadir-sidecar-content/current\nBDAG_IPFS_CONTENT_ARTIFACT_MANIFEST=./data-restore/rawdatadir-sidecar-content/current/manifest.json\nBDAG_IPFS_CONTENT_ALLOW_UNSIGNED_ARTIFACT=0\nBDAG_IPFS_CONTENT_PUBLISH_IPNS=0\nBDAG_IPFS_CONTENT_IPNS_KEY=\nBDAG_IPFS_CONTENT_REPUBLISH_IPNS_WHILE_WAITING=1\nBDAG_IPFS_CONTENT_IPNS_TTL=1m\nBDAG_IPFS_CONTENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_CONTENT_ADD_ARGS=\"--recursive --cid-version=1 --raw-leaves --pin=true --quieter\"\nBDAG_IPFS_CONTENT_DISCOVERY_FILE=./ops/ipfs-content-discovery.json\nBDAG_IPFS_CONTENT_LATEST_IPNS=/ipns/k51qzi5uqu5djjlh4vxtmzyswx0qk4s3wdlf3yrpkszp38gq5sl71zcgmmc3jk\nBDAG_IPFS_CONTENT_DEFAULT_INDEX_CID=bafkreia7jk2ljqi3raiohugp6nw3633njfp7jmnuvqh47po52et4kupu2a\nBDAG_IPFS_CONTENT_DEFAULT_ROOT_CID=\nBDAG_IPFS_CONTENT_STATUS_FILE=./ops/runtime/ipfs-content-sidecar-status.json\nBDAG_IPFS_CONTENT_LATEST_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\nBDAG_IPFS_SEGMENT_WRITER_MODE=auto\nBDAG_IPFS_SEGMENT_START_POLICY=live_tail\nBDAG_IPFS_SEGMENT_FINALITY_LAG_ORDERS=600\nBDAG_IPFS_SEGMENT_ORDERS_PER_SEGMENT=300\nBDAG_IPFS_SEGMENT_MAX_SEGMENTS_PER_RUN=1\nBDAG_IPFS_SEGMENT_MAX_RPC_PER_SECOND=25\nBDAG_IPFS_SEGMENT_RPC_TIMEOUT=8\nBDAG_IPFS_SEGMENT_BLOCK_RPC_RETRIES=2\nBDAG_IPFS_SEGMENT_PUBLISH_IPNS=0\nBDAG_IPFS_SEGMENT_IPNS_KEY=\nBDAG_IPFS_SEGMENT_IPNS_TTL=1m\nBDAG_IPFS_SEGMENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_SEGMENT_STATUS_FILE=./ops/runtime/ipfs-content/segment-writer-status.json\nBDAG_IPFS_SEGMENT_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\n\n# FastSync candidates for new/empty nodes and startup peers. Entries must be\n# complete P2P multiaddrs with peer IDs. Address class is not a sync mode or\n# priority signal; libp2p/FastArtifact probing should pick useful peers by\n# measured P2P latency, manifest/chunk response, and sustained transfer.\nBDAG_FASTSYNC_PEER_ORDERING=p2p-latency\nBDAG_FASTSYNC_APPEND_ADDPEERS=1\nBDAG_FASTARTIFACTSYNC_ENABLED=1\nBOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_RANGE_BLOCKS=1024\nBDAG_FASTSYNC_PREPROCESS_WORKERS=1\nBDAG_SYNC_COORDINATOR_ACCELERATE_FASTSYNC=1\nBDAG_SYNC_COORDINATOR_FAST_RESTART_COOLDOWN_SECONDS=900\nBDAG_SYNC_COORDINATOR_RESTART_ON_STALE_IMPORT=1\nBDAG_CATCHUP_PAUSE_ENABLED=1\nBDAG_CATCHUP_PAUSE_ON_SYNCING=1\nBDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300\nBDAG_CATCHUP_IO_PRESSURE_PAUSE_ENABLED=1\nBDAG_CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS=25\nBDAG_CATCHUP_IOWAIT_WARN_PERCENT=15\nBDAG_CATCHUP_IO_SOME_AVG10_WARN=20.0\nBDAG_CATCHUP_IO_FULL_AVG10_WARN=10.0\nBDAG_CATCHUP_NODE_RECREATE_ENABLED=1\nBDAG_CATCHUP_NODE_CACHE_MB=6144\nBDAG_CATCHUP_NODE_CACHE_MIN_MB=1024\nBDAG_CATCHUP_NODE_CACHE_MEMORY_PERCENT=40\nBDAG_FAST_CATCHUP_ARTIFACT_MODE=auto\nBDAG_FAST_CATCHUP_ARTIFACT_RETRY_SECONDS=300\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_BEHIND_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_GAIN_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_TRUST_ON_FIRST_SIGNED=1\nBDAG_FAST_CATCHUP_ALLOW_UNSIGNED_ARTIFACTS=0\nBDAG_FAST_CATCHUP_ARTIFACT_TIMEOUT=21600s\nNODE_RPC_USER=test\nNODE_RPC_PASS=\nNODE_RPC_URLS=http://127.0.0.1:38131\n# Optional block-candidate submit override. Leave blank to use NODE_RPC_URLS.\"\n# Named entries are allowed: local=http://node:38131\nPOOL_SUBMIT_RPC_URLS=\n# Extra node flags.\nNODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\nBDAG_FASTSYNC_SNAPSHOT_PATH=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_ID=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_HEX=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST_TTL=24h\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS_FILE=/etc/bdagStack/node.conf\nBDAG_FASTSNAP_AUTO_MAX_PEERS=3\nBDAG_FASTSNAP_LEGACY_FALLBACK=0\n# Production pool deployments are mainnet-only. Install/runtime scripts reject\n# any rawdatadir or FastSnap network value other than mainnet.\nBDAG_FASTSNAP_NETWORK=mainnet\nBDAG_RAWDATADIR_NETWORK=mainnet\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=300s\nBDAG_FASTSNAP_PARALLELISM=8\nBDAG_FASTSNAP_RETRIES=5\nBDAG_FASTSNAP_RETRY_DELAY=10s\nBDAG_FASTSNAP_TRUSTED_SIGNERS=\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_KEEP_ARCHIVE=0\nPOSTGRES_USER=bdag_pool\nPOSTGRES_DB=bdagpool\nPOSTGRES_PASSWORD=\n# ---------------------------------------------------------------------------\n# Mining pool \u2014 passed into the `pool` container environment.\n# ---------------------------------------------------------------------------\nPOOL_BIND_ADDR=192.168.1.100:3334\nPPLNS_N_WORK=1000\nPOOL_FEE_PERCENTAGE=1.0\nPOOL_BLOCK_MATURITY=10\nPOOL_PAYOUT_MATURITY=4096\n# CPU miner (only used when COMPOSE_PROFILES includes \"miner\").\nMINER_POOL_URL=stratum+tcp://192.168.1.100:3334\nMINER_POOL_PASS=\nMINER_WORKERS=1\n\n\n\n# ASIC block-candidate timing pinned to the best observed 2026-06-21\n# 05:40-05:57 SAST production window.\nPOOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true\nPOOL_TEMPLATE_TTL_REFRESH_MS=100\nPOOL_SUBMIT_STALE_BLOCK_CANDIDATES=false\nPOOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750\nPOOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100\nPOOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true\nPOOL_STALE_RACE_REJECT_WINDOW_SECONDS=10\nPOOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1\nPOOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0\nPOOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60\nPOOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true\nPOOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true\nSTACK_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nBLOCKDAG_CORECHAIN_CONTEXT=/home/jeremy/blockdag-source/blockdag-corechain\nPOOL_SRC_CONTEXT=/home/jeremy/blockdag-source/pool\nCPU_MINER_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nDASHBOARD_SRC_CONTEXT=/home/jeremy/blockdag-source/redis-dash\nCOMPOSE_PROFILES=\nNODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nSNAPSHOT_HOST_PATH=./docker/no-snapshot.marker\nBDAG_REQUIRE_SNAPSHOT=0\nBDAG_ENABLE_NODE_MINING=1\nBDAG_NODE_MODULES=Blockdag,miner\nBDAG_POOL_HOST=192.168.1.100\nBDAG_POOL_URL=stratum+tcp://192.168.1.100:3334\nBDAG_MINER_SCAN_TARGET=192.168.1.0/24\nBDAG_MINER_CONTROL_ENABLED=0\nDASHBOARD_BIND=127.0.0.1\nNODE_RPC_URL=http://127.0.0.1:38131\nPOOL_DB_HOST=127.0.0.1\nPOOL_DB_PORT=55432\nMINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_PRIVATE_KEY=\nPOOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\nPOOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\nPOOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp\nPOOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30\nPOOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com\nBDAG_POOL_START_GATE_MIN_PEERS=2\nBDAG_NODE_DEBUG_LEVEL=warn\nBDAG_NODE_NO_FILE_LOGGING=1\n", + "hard_safety_rule": "Live node must remain --modules=Blockdag --modules=miner --miner --miningaddr=; no p2p module in live/default/test/generated paths.", + "node_command_env": "[\"/usr/local/bin/docker-entrypoint-nodeworker.sh\",\"/usr/local/bin/nodeworker\",\"--node-binary=/usr/local/bin/blockdag-node\",\"--node-args=--configfile /etc/bdagStack/node.conf\",\"--rpc-url=ws://127.0.0.1:18546\",\"--dag-rpc-url=http://127.0.0.1:38131\",\"--persist-root=/var/lib/bdagStack/nodeworker\",\"--health-min-peers=2\",\"--rollout-window=30m\"] null [\"BDAG_ENTRYPOINT_CHOWN_MODE=needed\",\"BDAG_NODE_MINING_NO_PENDING_TX=0\",\"TEMP=/tmp\",\"BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral\",\"MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"TMP=/tmp\",\"BDAG_NODE_MODULES=Blockdag,miner\",\"MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\",\"BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\",\"SYNC_SOURCE_NODE=0\",\"BDAG_NODE_ARCHIVAL=0\",\"BDAG_SNAPSHOT_URL=\",\"P2P_PORT=8155\",\"BDAG_STORAGE_PROFILE=auto\",\"BDAG_DETECTED_NETWORK_TOPOLOGY=\",\"BDAG_ASIC_LAN_CIDRS=192.168.1.0/24\",\"BDAG_ENABLE_NODE_MINING=1\",\"BDAG_NODE_NO_FILE_LOGGING=1\",\"BDAG_NETWORK_TOPOLOGY=auto\",\"BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\",\"BDAG_P2P_ADVERTISE_IP=\",\"BDAG_P2P_INTERFACE=\",\"BDAG_NODE_MINING_ARGS=\",\"TMPDIR=/tmp\",\"BDAG_NODE_OBSOLETE_HEIGHT=20\",\"POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODE_DEBUG_LEVEL=debug\",\"BDAG_NETWORK=\",\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"]", + "node_conf": "# BlockDAG node configuration for the Docker Compose stack.\n\n# P2P listen\nlisten=0.0.0.0\nport=8150\n\n# BDAG RPC listen\nrpclisten=0.0.0.0:38131\n\n# EVM RPC listen\nevm.http.addr=0.0.0.0\nevm.http.port=18545\nevm.ws.addr=0.0.0.0\nevm.ws.port=18546\n\n\n# Bootstrap peers (mainnet seed nodes). Add one addpeer=... line per complete\n# BDAG P2P multiaddr. Keep only public peers that are currently reachable.\n\n\n\n\naddpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo\naddpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\naddpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz\naddpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD\naddpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU\naddpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm\naddpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv\naddpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn\naddpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta\naddpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ\naddpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME\naddpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus\naddpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG\naddpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ\naddpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY\naddpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G\naddpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7\naddpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1\naddpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP\naddpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X\naddpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB\naddpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9\n\n# Peer limit\nmaxpeers=350\ncache=6144\ncache.database=70\n\n# RPC auth. Must match NODE_RPC_USER / NODE_RPC_PASS in .env.\nrpcuser=test\nrpcpass=\n\n# Storage paths\ndatadir=/var/lib/bdagStack/node\nlogdir=/var/log/bdagStack\n\n# Native metrics endpoint\nmetrics=true\nprofile=0.0.0.0:6060\n\n# Keep empty unless node mining is intentionally enabled.\nminingaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\nmodules=Blockdag,miner\n\nevm.http.api=eth,net,web3,txpool,debug\nhttp.readtimeout=45s\nhttp.writetimeout=45s\nhttp.idletimeout=45s\nevmenv=\"--rpc.allow-unprotected-txs --metrics --metrics.addr 0.0.0.0 --metrics.port 6060 --cache 6144\"\nrpcmaxclients=64\nrpcmaxclientsperhost=16\nrpcmaxconcurrentreqs=500\nrpcrequesttimeout=60s\nhttp.ratelimit=1800\nhttp.rateburst=3200\n", + "p2p_guard": { + "clean_no_hits": true, + "code": 1, + "ok": false, + "out": "" + }, + "pool_metrics_tail": "pool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"1\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"2\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"3\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"5\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"10\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"25\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"50\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"100\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"250\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"1000\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\",le=\"+Inf\"} 1\npool_submit_job_sequence_distance_sum{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\"} 0\npool_submit_job_sequence_distance_count{classification=\"current\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-parent\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"1\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"2\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"3\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"5\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"10\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"25\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"50\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"100\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"250\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"1000\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\",le=\"+Inf\"} 18\npool_submit_job_sequence_distance_sum{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\"} 18\npool_submit_job_sequence_distance_count{classification=\"retained\",outcome=\"rejected\",pool_id=\"0\",reason=\"non_current_job\"} 18\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 1\npool_submit_job_sequence_distance_sum{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\"} 1\npool_submit_job_sequence_distance_count{classification=\"retained_same_parent_grace\",outcome=\"accepted-block\",pool_id=\"0\",reason=\"ok\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 0\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 8\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 9\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 19\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 9\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1\"} 0\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"2\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"3\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"5\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"10\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"25\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"50\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"100\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"250\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1000\"} 3\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"+Inf\"} 3\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 8\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 3\n# HELP pool_submit_read_to_handle_latency_seconds Time between reading a mining.submit request from the Stratum socket and starting submit handling.\n# TYPE pool_submit_read_to_handle_latency_seconds histogram\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0001\"} 631\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0005\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.001\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"1\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"2\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"5\"} 653\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 653\npool_submit_read_to_handle_latency_seconds_sum{pool_id=\"0\"} 0.030935093000000045\npool_submit_read_to_handle_latency_seconds_count{pool_id=\"0\"} 653\n# HELP pool_submit_stall_window_failures Submit-stall recovery failures observed in the current window by kind.\n# TYPE pool_submit_stall_window_failures gauge\npool_submit_stall_window_failures{kind=\"all\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"duplicate\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"stale\",pool_id=\"0\"} 0\n# HELP pool_template_broadcasts_total Total block templates broadcast to connected miners.\n# TYPE pool_template_broadcasts_total counter\npool_template_broadcasts_total{pool_id=\"0\"} 373\n# HELP pool_template_conversion_stall_active_miners Active authorized miners observed by the template-conversion stall detector.\n# TYPE pool_template_conversion_stall_active_miners gauge\npool_template_conversion_stall_active_miners{pool_id=\"0\"} 4\n# HELP pool_template_conversion_stall_failure_ratio Failed candidate block percentage in the current template-conversion stall window.\n# TYPE pool_template_conversion_stall_failure_ratio gauge\npool_template_conversion_stall_failure_ratio{pool_id=\"0\"} 1.7857142857142858\n# HELP pool_template_conversion_stall_window_candidates Candidate block outcomes observed in the current template-conversion stall window by kind.\n# TYPE pool_template_conversion_stall_window_candidates gauge\npool_template_conversion_stall_window_candidates{kind=\"accepted\",pool_id=\"0\"} 55\npool_template_conversion_stall_window_candidates{kind=\"duplicate\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"failed\",pool_id=\"0\"} 1\npool_template_conversion_stall_window_candidates{kind=\"stale\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"total\",pool_id=\"0\"} 56\n# HELP pool_template_fetch_duration_seconds Time to fetch a block template from the node via RPC.\n# TYPE pool_template_fetch_duration_seconds histogram\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 92\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 113\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 187\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 211\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 222\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 232\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 248\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"1\"} 261\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"2\"} 267\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"5\"} 269\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 269\npool_template_fetch_duration_seconds_sum{pool_id=\"0\"} 32.643264431999995\npool_template_fetch_duration_seconds_count{pool_id=\"0\"} 269\n# HELP pool_vardiff_adjustments_total VarDiff difficulty adjustments by direction.\n# TYPE pool_vardiff_adjustments_total counter\npool_vardiff_adjustments_total{direction=\"increase\",pool_id=\"0\"} 20\n# HELP pool_vardiff_floor_hits_total VarDiff retarget attempts clamped by the configured minimum pool difficulty.\n# TYPE pool_vardiff_floor_hits_total counter\npool_vardiff_floor_hits_total{pool_id=\"0\"} 0\n# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.\n# TYPE process_cpu_seconds_total counter\nprocess_cpu_seconds_total 3.14\n# HELP process_max_fds Maximum number of open file descriptors.\n# TYPE process_max_fds gauge\nprocess_max_fds 524287\n# HELP process_network_receive_bytes_total Number of bytes received by the process over the network.\n# TYPE process_network_receive_bytes_total counter\nprocess_network_receive_bytes_total 5.3831119631e+10\n# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network.\n# TYPE process_network_transmit_bytes_total counter\nprocess_network_transmit_bytes_total 4.0149227619e+10\n# HELP process_open_fds Number of open file descriptors.\n# TYPE process_open_fds gauge\nprocess_open_fds 17\n# HELP process_resident_memory_bytes Resident memory size in bytes.\n# TYPE process_resident_memory_bytes gauge\nprocess_resident_memory_bytes 3.7425152e+07\n# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.\n# TYPE process_start_time_seconds gauge\nprocess_start_time_seconds 1.78208470456e+09\n# HELP process_virtual_memory_bytes Virtual memory size in bytes.\n# TYPE process_virtual_memory_bytes gauge\nprocess_virtual_memory_bytes 2.522488832e+09\n# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.\n# TYPE process_virtual_memory_max_bytes gauge\nprocess_virtual_memory_max_bytes 1.8446744073709552e+19\n# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.\n# TYPE promhttp_metric_handler_requests_in_flight gauge\npromhttp_metric_handler_requests_in_flight 1\n# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.\n# TYPE promhttp_metric_handler_requests_total counter\npromhttp_metric_handler_requests_total{code=\"200\"} 74\npromhttp_metric_handler_requests_total{code=\"500\"} 0\npromhttp_metric_handler_requests_total{code=\"503\"} 0", + "postdeploy_observation": { + "dashboard": "After dashboard restart, Redis-live warmed from 0 ready lanes to 4 ready lanes by the next two 10-second samples while mining stayed mode=mining overall=ok.", + "final_sample": { + "can_accept_shares": true, + "can_submit_blocks": true, + "mode": "mining", + "overall": "ok", + "peer_count": 17, + "sync_status": "synced" + }, + "pool": "After pool restart, six 15-second samples stayed at 4 ready lanes; valid shares increased 5->242 and submit successes 1->35 with submit_errors=0." + }, + "predeploy_savepoint": "ops/savepoints/20260622-012730-pre-dashboard-pool-deploy-retained.json", + "purpose": "Post-deploy working stack after redis-dash sidebar layout and pool P2P-safe transient node-syncing job preservation were built, deployed, and observed mining.", + "rendered_compose_config": "name: stack\nservices:\n dashboard:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: dashboard\n cpu_shares: 256\n container_name: dashboard\n depends_on:\n node:\n condition: service_started\n required: true\n pool:\n condition: service_started\n required: true\n environment:\n ADDR: 0.0.0.0:8088\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS: \"3\"\n BDAG_CHAIN_POLL_SECONDS: \"2\"\n BDAG_CHAIN_WS_READ_LIMIT_BYTES: \"1048576\"\n BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS: \"10\"\n BDAG_DASHBOARD_HEALTH_STALE_SECONDS: \"20\"\n BDAG_DASHBOARD_REDIS_ENABLED: \"1\"\n BDAG_DASHBOARD_REDIS_MANAGED: \"1\"\n BDAG_EVM_HTTP_URL: http://host.docker.internal:18545\n BDAG_EVM_SYNC_BACKOFF_SECONDS: \"60\"\n BDAG_EVM_WS_URL: ws://host.docker.internal:18546\n BDAG_GLOBAL_BLOCK_WINDOW: \"600\"\n BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS: \"30\"\n BDAG_GLOBAL_FAST_WARMUP_BLOCKS: \"120\"\n BDAG_GLOBAL_MAX_INCREMENTAL_GAP: \"120\"\n BDAG_GLOBAL_RPC_CHUNK_SIZE: \"64\"\n BDAG_GLOBAL_RPC_WORKERS: \"4\"\n BDAG_GLOBAL_SETTLE_DELAY_SECONDS: \"5\"\n BDAG_MINER_CONTROL_ENABLED: \"0\"\n BDAG_NODE_RPC_URL: http://host.docker.internal:38131\n BDAG_POOL_METRICS_URL: http://host.docker.internal:9090/metrics\n BDAG_POOL_URL: stratum+tcp://192.168.1.100:3334\n BDAG_REDIS_APPENDFSYNC: everysec\n BDAG_REDIS_APPENDONLY: \"yes\"\n BDAG_REDIS_CONFIG: /etc/redis/redis.conf\n BDAG_REDIS_MAXMEMORY: 256mb\n BDAG_REDIS_MAXMEMORY_POLICY: noeviction\n BDAG_REDIS_SOCKET: /run/dashboard-redis/redis.sock\n BDAG_TREND_BACKFILL_CHUNK_SIZE: \"128\"\n BDAG_TREND_BACKFILL_DAYS: \"30\"\n BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS: \"4096\"\n BDAG_TREND_BACKFILL_SLEEP_MS: \"100\"\n BDAG_TREND_CATCHUP_INTERVAL_SECONDS: \"30\"\n BDAG_TREND_HISTORY_ENABLED: \"1\"\n BDAG_TREND_RECENT_WARMUP_BLOCKS: \"3600\"\n BDAG_TREND_RPC_WORKERS: \"4\"\n BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS: \"2048\"\n BDAG_TREND_SPARSE_SAMPLES_PER_PASS: \"24\"\n DASHBOARD_TIMEZONE: Africa/Johannesburg\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n POOL_ASIC_ARP_TABLE_PATH: /host/proc/net/arp\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n healthcheck:\n test:\n - CMD-SHELL\n - curl -fsS http://127.0.0.1:8088/ >/dev/null\n timeout: 5s\n interval: 10s\n retries: 12\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 8088\n published: \"8088\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n - /run/dashboard-redis:size=16m,mode=0700\n volumes:\n - type: volume\n source: dashboard-redis\n target: /var/lib/dashboard-redis\n volume: {}\n - type: bind\n source: /proc/net/arp\n target: /host/proc/net/arp\n read_only: true\n bind:\n create_host_path: true\n miner-route:\n cap_add:\n - NET_ADMIN\n container_name: miner-route\n entrypoint:\n - /bin/sh\n - -c\n - 'while true; do if [ -n \"$$BDAG_MINER_ROUTE_SUBNET\" ] && [ -n \"$$BDAG_MINER_ROUTE_GATEWAY\" ]; then cmd=\"ip route replace $$BDAG_MINER_ROUTE_SUBNET via $$BDAG_MINER_ROUTE_GATEWAY\"; [ -n \"$$BDAG_MINER_ROUTE_DEV\" ] && cmd=\"$$cmd dev $$BDAG_MINER_ROUTE_DEV\"; $$cmd || echo \"miner-route: failed: $$cmd\"; fi; sleep 60; done'\n environment:\n BDAG_MINER_ROUTE_DEV: \"\"\n BDAG_MINER_ROUTE_GATEWAY: \"\"\n BDAG_MINER_ROUTE_SUBNET: \"\"\n image: alpine:3.20\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n restart: unless-stopped\n node:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: node\n cpu_shares: 4096\n container_name: node\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_ASIC_LAN_INTERFACE: enxc8a362eb4bdc\n BDAG_DETECTED_NETWORK_TOPOLOGY: \"\"\n BDAG_ENABLE_NODE_MINING: \"1\"\n BDAG_ENTRYPOINT_CHOWN_MODE: needed\n BDAG_EPHEMERAL_DIR: /run/bdag-ephemeral\n BDAG_NETWORK: \"\"\n BDAG_NETWORK_TOPOLOGY: auto\n BDAG_NODE_ARCHIVAL: \"0\"\n BDAG_NODE_DEBUG_LEVEL: warn\n BDAG_NODE_MINING_ARGS: \"\"\n BDAG_NODE_MINING_NO_PENDING_TX: \"0\"\n BDAG_NODE_MODULES: Blockdag,miner\n BDAG_NODE_NO_FILE_LOGGING: \"1\"\n BDAG_NODE_OBSOLETE_HEIGHT: \"20\"\n BDAG_NODEWORKER_LIVENESS_TIMEOUT: 5m\n BDAG_P2P_ADVERTISE_IP: \"\"\n BDAG_P2P_INTERFACE: \"\"\n BDAG_SNAPSHOT_URL: \"\"\n BDAG_STORAGE_PROFILE: auto\n BOOTSTRAP_PEER_ADDRESSES: /ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\n MINING_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_ARGS_APPEND: --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n P2P_PORT: \"8155\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n SYNC_SOURCE_NODE: \"0\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -900\n platform: linux/amd64\n restart: unless-stopped\n shm_size: \"536870912\"\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\n volumes:\n - type: bind\n source: /home/jeremy/blockdag-source/stack/data/node\n target: /var/lib/bdagStack/node\n bind:\n create_host_path: true\n - type: volume\n source: nodeworker-data\n target: /var/lib/bdagStack/nodeworker\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker\n target: /snapshot/latest.bdsnap\n read_only: true\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/node.conf\n target: /etc/bdagStack/node.conf\n read_only: true\n bind:\n create_host_path: true\n pool:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: pool\n cpu_shares: 3072\n container_name: pool\n depends_on:\n pool-db:\n condition: service_healthy\n required: true\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n METRICS_ADDR: 0.0.0.0:9090\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_URL: http://127.0.0.1:38131\n NODE_RPC_URLS: http://127.0.0.1:38131\n NODE_RPC_USER: test\n PG_URL: postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable\n POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB: \"true\"\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE: \"true\"\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS: \"8000\"\n POOL_BIND_ADDR: 192.168.1.100:3334\n POOL_BLOCK_MATURITY: \"10\"\n POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_ENABLED: \"true\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_STEP_MS: \"250\"\n POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT: \"5\"\n POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS: \"60\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: \"3\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_FEE_PERCENTAGE: \"1.0\"\n POOL_INSTANCE_ID: \"\"\n POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS: \"1750\"\n POOL_PAYOUT_MATURITY: \"4096\"\n POOL_PAYOUT_MAX_BLOCKS_PER_TICK: \"5\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED: \"false\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS: \"0\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED: \"true\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS: \"20\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS: \"1000\"\n POOL_PRIVATE_KEY: 11f16642b7153d33ef1f2029fad20e77489e38c801afe93ac5d1264238e6d780\n POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: \"1750\"\n POOL_RPC_BACKENDS: \"\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: \"false\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS: \"30\"\n POOL_RPC_ROUTER_EVM_REFERENCE_URLS: https://rpc.blockdag.engineering,https://rpc.bdagscan.com\n POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: \"true\"\n POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS: \"15\"\n POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS: \"30\"\n POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS: \"2\"\n POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS: \"15\"\n POOL_RUNTIME_ADMIN_ENABLED: \"false\"\n POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: \"0\"\n POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: \"1\"\n POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS: \"5\"\n POOL_STALE_RACE_REJECT_WINDOW_SECONDS: \"10\"\n POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS: \"60\"\n POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: \"true\"\n POOL_SUBMIT_RPC_URLS: \"\"\n POOL_SUBMIT_STALE_BLOCK_CANDIDATES: \"false\"\n POOL_TEMPLATE_TTL_REFRESH_MS: \"100\"\n PPLNS_N_WORK: \"1000\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n WALLET_RPC_URL: http://127.0.0.1:18545\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -800\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n pool-db:\n cpu_shares: 3072\n container_name: postgres\n environment:\n POSTGRES_DB: bdagpool\n POSTGRES_PASSWORD: 06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803\n POSTGRES_USER: bdag_pool\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n healthcheck:\n test:\n - CMD-SHELL\n - pg_isready -U bdag_pool -d bdagpool\n timeout: 5s\n interval: 5s\n retries: 20\n image: postgres:15-bookworm\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: -800\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 5432\n published: \"55432\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n volumes:\n - type: volume\n source: postgres-data\n target: /var/lib/postgresql/data\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/sql/pool-schema.sql\n target: /docker-entrypoint-initdb.d/01-pool-schema.sql\n read_only: true\n bind:\n create_host_path: true\n sentinel:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: sentinel\n cpu_shares: 256\n container_name: sentinel\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep $${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_SENTINEL_INTERVAL_SECONDS: \"60\"\n BDAG_SENTINEL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_SENTINEL_USER_SERVICES: \"\"\n BDAG_SENTINEL_USER_TIMERS: \"\"\n BDAG_STACK_SERVICES: postgres,node,pool\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n status-sampler:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: status-sampler\n blkio_config:\n weight: 100\n cpu_shares: 256\n container_name: status-sampler\n entrypoint:\n - /bin/sh\n - -c\n - exec python3 ops/status_sampler.py --loop --no-logs\n environment:\n BDAG_CHAIN_STATE_SELF_HEAL_COMMAND: ops/chain-state-self-heal.sh --from-systemd\n BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_CHAIN_STATE_SELF_HEAL_UNIT: \"\"\n BDAG_MINING_IMPERATIVE_GUARD_UNITS: \"\"\n BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS: \"30\"\n BDAG_NODE_RPC_URLS: node=http://host.docker.internal:38131\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS: \"60\"\n BDAG_STATUS_SAMPLER_FILE: /home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json\n BDAG_STATUS_SAMPLER_INTERVAL_SECONDS: \"10\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n watchdog:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: watchdog\n cpu_shares: 256\n container_name: watchdog\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST: 192.168.1.100\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SOURCE_URL: http://dashboard:8088/api/status\n BDAG_WATCHDOG_INTERVAL: \"60\"\n BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS: \"300\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\nnetworks:\n default:\n name: stack_default\nvolumes:\n dashboard-redis:\n name: stack_dashboard-redis\n nodeworker-data:\n name: stack_nodeworker-data\n postgres-data:\n name: stack_postgres-data\nx-build-args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\nx-dev-source-contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\nx-mining-logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\nx-node-critical:\n blkio_config:\n weight: 1000\n cpu_shares: 4096\n oom_score_adj: -900\n shm_size: 512m\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\nx-ops-low:\n blkio_config:\n weight: 100\n cpu_shares: 256\n oom_score_adj: 300\nx-pool-critical:\n blkio_config:\n weight: 900\n cpu_shares: 3072\n oom_score_adj: -800", + "repo_state": { + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "diff_stat": "", + "head": "7426d7fa1b39a29b57b5e81f24dc2baaede0c54c", + "last_commit": "7426d7f Protect mining health from zero coinbase templates", + "path": "/home/jeremy/blockdag-source/blockdag-corechain", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-corechain: codex post-savepoint zero-coinbase corechain patchset 20260621", + "status": "## codex/native-safe-mining-20260620-corechain...origin/codex/native-safe-mining-20260620-corechain [ahead 1]" + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "diff_stat": "", + "head": "937dff6d0cc248b35d38dbc681f3fe26a4ed6485", + "last_commit": "937dff6 Preserve safe ASIC jobs during transient node sync", + "path": "/home/jeremy/blockdag-source/pool", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-pool: codex post-savepoint zero-coinbase pool patchset 20260621", + "status": "## codex/native-safe-mining-20260620-pool...origin/codex/native-safe-mining-20260620-pool [ahead 2]" + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "diff_stat": "", + "head": "7f3783333eddedaed162a04c4f3f70e5860bff23", + "last_commit": "7f37833 Adopt sidebar mining dashboard layout", + "path": "/home/jeremy/blockdag-source/redis-dash", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-dashboard: codex post-savepoint dashboard safety patchset 20260621", + "status": "## codex/native-safe-mining-20260620-dashboard...origin/codex/native-safe-mining-20260620-dashboard [ahead 2]" + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "diff_stat": "", + "head": "00a67593451555e82ed5bf864668dfb1ff8811cf", + "last_commit": "00a6759 Harden redis stack mining safety defaults", + "path": "/home/jeremy/blockdag-source/stack-redis", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-stack: codex post-savepoint zero-coinbase/watchdog patchset 20260621", + "status": "## codex/native-safe-mining-20260620-stack...origin/codex/native-safe-mining-20260620-stack [ahead 2]\n?? ops/savepoints/20260622-012730-pre-dashboard-pool-deploy-retained.json" + } + }, + "retains_original_baseline": "ops/savepoints/20260621-233309-tonight-baseline-retained.json", + "rollback_commands": [ + "docker tag stack-dashboard:post-sidebar-pool-working-20260622-013454 stack-dashboard:latest && docker tag stack-pool:post-sidebar-pool-working-20260622-013454 stack-pool:latest && docker compose up -d --no-deps --no-build dashboard && docker compose up -d --no-deps --no-build pool", + "Dashboard-only rollback: docker tag stack-dashboard:pre-dashboard-design-20260622-012730 stack-dashboard:latest && docker compose up -d --no-deps --no-build dashboard", + "Pool-only rollback: docker compose stop pool && docker tag stack-pool:pre-node-sync-preserve-20260622-012730 stack-pool:latest && docker compose up -d --no-deps --no-build pool", + "If node rollback is ever required: stop pool first, restore node tag/config deliberately, verify native template health and fresh peers, then restart pool." + ], + "rollback_tags": { + "dashboard": "stack-dashboard:post-sidebar-pool-working-20260622-013454", + "node": "stack-node:post-sidebar-pool-working-20260622-013454", + "pool": "stack-pool:post-sidebar-pool-working-20260622-013454", + "pre_dashboard": "stack-dashboard:pre-dashboard-design-20260622-012730", + "pre_pool": "stack-pool:pre-node-sync-preserve-20260622-012730", + "status_sampler": "stack-status-sampler:post-sidebar-pool-working-20260622-013454" + }, + "savepoint_id": "20260622-013454-post-sidebar-pool-working-retained", + "screenshots": { + "live_desktop": "/tmp/redis-dash-live-desktop-postdeploy.png", + "live_mobile": "/tmp/redis-dash-live-mobile-postdeploy.png" + }, + "verified_tests": [ + "pool: go test ./... -count=1", + "redis-dash: go test ./... -count=1", + "pool and redis-dash: git diff --check", + "stack-redis p2p guard: git grep had no active hits", + "redis-dash no live-tab browser polling markers: hx-trigger every, setInterval, htmx.ajax absent", + "Playwright screenshots captured desktop and mobile after live dashboard deploy" + ] +} diff --git a/ops/savepoints/20260622-061448-dashboard-control-suite-baseline-retained.json b/ops/savepoints/20260622-061448-dashboard-control-suite-baseline-retained.json new file mode 100644 index 0000000..21ee6d9 --- /dev/null +++ b/ops/savepoints/20260622-061448-dashboard-control-suite-baseline-retained.json @@ -0,0 +1,7301 @@ +{ + "baseline_notes": [ + "This is the safe point requested before dashboard control-suite expansion.", + "Live state before source edits: mining ok, accepting shares, submit-ready, four ready ASIC lanes, native synced, peers available.", + "Memory gh repo sync was attempted without --force and refused due diverging changes; no forced memory rewrite was done." + ], + "container_state": { + "dashboard": { + "image_latest_id": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "image_safepoint_id": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/dashboard" + ], + "Env": [ + "BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS=10", + "BDAG_GLOBAL_FAST_WARMUP_BLOCKS=120", + "BDAG_TREND_BACKFILL_CHUNK_SIZE=128", + "BDAG_GLOBAL_SETTLE_DELAY_SECONDS=5", + "BDAG_REDIS_MAXMEMORY_POLICY=noeviction", + "BDAG_GLOBAL_MAX_INCREMENTAL_GAP=120", + "BDAG_REDIS_APPENDFSYNC=everysec", + "TEMP=/tmp", + "DASHBOARD_TIMEZONE=Africa/Johannesburg", + "NODE_RPC_PASS=", + "BDAG_REDIS_APPENDONLY=yes", + "BDAG_MINER_CONTROL_ENABLED=0", + "BDAG_CHAIN_POLL_SECONDS=2", + "BDAG_POOL_METRICS_URL=http://host.docker.internal:9090/metrics", + "BDAG_EVM_HTTP_URL=http://host.docker.internal:18545", + "BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS=2048", + "BDAG_REDIS_SOCKET=/run/dashboard-redis/redis.sock", + "BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS=", + "BDAG_EVM_SYNC_BACKOFF_SECONDS=60", + "TMPDIR=/tmp", + "BDAG_TREND_RPC_WORKERS=4", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS=3", + "BDAG_EVM_WS_URL=ws://host.docker.internal:18546", + "BDAG_TREND_BACKFILL_DAYS=30", + "BDAG_TREND_HISTORY_ENABLED=1", + "BDAG_GLOBAL_RPC_WORKERS=4", + "BDAG_TREND_BACKFILL_SLEEP_MS=100", + "BDAG_REDIS_MAXMEMORY=256mb", + "BDAG_REDIS_CONFIG=/etc/redis/redis.conf", + "BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30", + "BDAG_GLOBAL_RPC_CHUNK_SIZE=64", + "BDAG_TREND_CATCHUP_INTERVAL_SECONDS=30", + "NODE_RPC_USER=test", + "BDAG_TREND_RECENT_WARMUP_BLOCKS=3600", + "BDAG_NODE_RPC_URL=http://host.docker.internal:38131", + "BDAG_GLOBAL_BLOCK_WINDOW=600", + "BDAG_DASHBOARD_REDIS_MANAGED=1", + "TMP=/tmp", + "ADDR=0.0.0.0:8088", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_DASHBOARD_REDIS_ENABLED=1", + "BDAG_DASHBOARD_HEALTH_STALE_SECONDS=20", + "BDAG_POOL_URL=stratum+tcp://192.168.1.100:3334", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "BDAG_TREND_SPARSE_SAMPLES_PER_PASS=", + "POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp", + "BDAG_CHAIN_WS_READ_LIMIT_BYTES=1048576", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "8088/tcp": {} + }, + "Healthcheck": { + "Interval": 10000000000, + "Retries": 12, + "Test": [ + "CMD-SHELL", + "curl -fsS http://127.0.0.1:8088/ >/dev/null" + ], + "Timeout": 5000000000 + }, + "Hostname": "4dcde8c64f82", + "Image": "stack-dashboard", + "Labels": { + "com.docker.compose.config-hash": "e03f4a8bb3888d03330d9472539c32e0a4893687873b226ce12c92c9412a2162", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "dashboard", + "com.docker.compose.service": "dashboard", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "" + }, + "Created": "2026-06-21T23:29:41.860726868Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "stack_dashboard-redis:/var/lib/dashboard-redis:rw", + "/proc/net/arp:/host/proc/net/arp:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/run/dashboard-redis": "size=16m,mode=0700", + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/hostname", + "HostsPath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/hosts", + "Id": "4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425", + "Image": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "ImageManifestDescriptor": { + "digest": "sha256:357094195a3d5564faa8c789ec583186a33793bb80175940a123358c6df9ea79", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 1242 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/lib/dashboard-redis", + "Driver": "local", + "Mode": "rw", + "Name": "stack_dashboard-redis", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_dashboard-redis/_data", + "Type": "volume" + }, + { + "Destination": "/host/proc/net/arp", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/proc/net/arp", + "Type": "bind" + } + ], + "Name": "/dashboard", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "dashboard", + "dashboard" + ], + "DNSNames": [ + "dashboard", + "4dcde8c64f82" + ], + "DriverOpts": null, + "EndpointID": "ae134f93be390b4a1adedfc1e1a4211e470561379eab34566e96b48f174c09f9", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.4", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "5e:6f:a7:ee:14:dd", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": { + "8088/tcp": [ + { + "HostIp": "127.0.0.1", + "HostPort": "8088" + } + ] + }, + "SandboxID": "ff534d22b1daa3f71f8ed45a63af8bdd3e152ec291ba7f3ad157416a9575294d", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/dashboard", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/4dcde8c64f82fa4cee383e8b4fbf94ffb646c09f174d85f5ae3d9d6f7c1e7425/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "Health": { + "FailingStreak": 0, + "Log": [ + { + "End": "2026-06-22T06:15:03.317708465+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T06:15:03.251991749+02:00" + }, + { + "End": "2026-06-22T06:15:13.492221243+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T06:15:13.318208224+02:00" + }, + { + "End": "2026-06-22T06:15:23.577040988+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T06:15:23.493062818+02:00" + }, + { + "End": "2026-06-22T06:15:33.981025466+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T06:15:33.578071521+02:00" + }, + { + "End": "2026-06-22T06:15:44.048769571+02:00", + "ExitCode": 0, + "Output": "", + "Start": "2026-06-22T06:15:43.982125517+02:00" + } + ], + "Status": "healthy" + }, + "OOMKilled": false, + "Paused": false, + "Pid": 3624915, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T23:29:43.671184302Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026/06/22 03:45:42 live trend gap at 11859690 after 11859653; rebuilding recent trend window\n2026/06/22 03:46:15 live trend gap at 11859736 after 11858823; rebuilding recent trend window\n2026/06/22 03:46:46 live trend gap at 11859777 after 11859735; rebuilding recent trend window\n2026/06/22 03:47:20 live websocket head 11859796 is ahead of retrievable EVM block lookup by 5 blocks: no retrievable EVM block at or below eth_blockNumber 11859819 within 16 blocks: eth_getBlockByNumber(0xb4f75c) returned null; eth_getBlockByNumber(latest) fallback failed: Post \"http://host.docker.internal:18545\": context deadline exceeded (Client.Timeout exceeded while awaiting headers); observed fallback failed: eth_getBlockByNumber(0xb4f754) returned null\n2026/06/22 03:47:20 trend history catchup failed: no retrievable EVM block at or below eth_blockNumber 11859820 within 16 blocks: eth_getBlockByNumber(0xb4f75d) returned null; eth_getBlockByNumber(latest) fallback failed: Post \"http://host.docker.internal:18545\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n2026/06/22 03:47:22 global Redis window source height moved backward from 11859791 to 11797755; rebuilding bounded live window\n2026/06/22 03:50:52 global Redis window gap 62088 exceeds incremental cap 120; rebuilding bounded window at 11860097\n2026/06/22 03:50:54 live trend gap at 11860098 after 11859735; rebuilding recent trend window\n15:M 22 Jun 2026 03:50:56.837 * Starting automatic rewriting of AOF on 1821% growth\n15:M 22 Jun 2026 03:50:56.840 * Creating AOF incr file appendonly.aof.149.incr.aof on background rewrite\n15:M 22 Jun 2026 03:50:56.843 * Background append only file rewriting started by pid 10792\n10792:C 22 Jun 2026 03:50:56.897 * Successfully created the temporary AOF base file temp-rewriteaof-bg-10792.aof\n10792:C 22 Jun 2026 03:50:56.898 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n15:M 22 Jun 2026 03:50:56.944 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 03:50:56.944 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-10792.aof into appendonly.aof.149.base.rdb\n15:M 22 Jun 2026 03:50:56.946 * Removing the history file appendonly.aof.148.incr.aof in the background\n15:M 22 Jun 2026 03:50:56.946 * Removing the history file appendonly.aof.148.base.rdb in the background\n15:M 22 Jun 2026 03:50:56.948 * Background AOF rewrite finished successfully\n2026/06/22 03:51:37 live trend gap at 11860102 after 11860100; rebuilding recent trend window\n2026/06/22 03:52:09 live trend gap at 11860170 after 11860151; rebuilding recent trend window\n2026/06/22 03:52:42 live trend gap at 11860210 after 11860151; rebuilding recent trend window\n2026/06/22 03:53:13 live trend gap at 11860251 after 11860234; rebuilding recent trend window\n2026/06/22 03:53:47 live trend gap at 11860298 after 11860234; rebuilding recent trend window\n2026/06/22 03:54:18 live trend gap at 11860343 after 11860321; rebuilding recent trend window\n2026/06/22 03:54:48 live trend gap at 11860382 after 11860321; rebuilding recent trend window\n15:M 22 Jun 2026 03:55:00.988 * Starting automatic rewriting of AOF on 1911% growth\n15:M 22 Jun 2026 03:55:00.991 * Creating AOF incr file appendonly.aof.150.incr.aof on background rewrite\n15:M 22 Jun 2026 03:55:00.992 * Background append only file rewriting started by pid 10963\n10963:C 22 Jun 2026 03:55:01.028 * Successfully created the temporary AOF base file temp-rewriteaof-bg-10963.aof\n10963:C 22 Jun 2026 03:55:01.029 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n15:M 22 Jun 2026 03:55:01.093 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 03:55:01.093 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-10963.aof into appendonly.aof.150.base.rdb\n15:M 22 Jun 2026 03:55:01.095 * Removing the history file appendonly.aof.149.incr.aof in the background\n15:M 22 Jun 2026 03:55:01.096 * Removing the history file appendonly.aof.149.base.rdb in the background\n15:M 22 Jun 2026 03:55:01.098 * Background AOF rewrite finished successfully\n2026/06/22 03:55:18 live trend gap at 11860422 after 11860395; rebuilding recent trend window\n2026/06/22 03:55:49 live trend gap at 11860458 after 11860395; rebuilding recent trend window\n2026/06/22 03:56:21 live trend gap at 11860498 after 11860480; rebuilding recent trend window\n2026/06/22 03:56:52 live trend gap at 11860538 after 11860480; rebuilding recent trend window\n2026/06/22 03:57:22 live trend gap at 11860573 after 11860550; rebuilding recent trend window\n2026/06/22 03:57:56 live trend gap at 11860597 after 11860550; rebuilding recent trend window\n2026/06/22 03:58:26 live trend gap at 11860629 after 11860608; rebuilding recent trend window\n2026/06/22 03:58:56 live trend gap at 11860670 after 11860608; rebuilding recent trend window\n15:M 22 Jun 2026 03:59:08.107 * Starting automatic rewriting of AOF on 1905% growth\n15:M 22 Jun 2026 03:59:08.157 * Creating AOF incr file appendonly.aof.151.incr.aof on background rewrite\n15:M 22 Jun 2026 03:59:08.158 * Background append only file rewriting started by pid 11132\n11132:C 22 Jun 2026 03:59:08.227 * Successfully created the temporary AOF base file temp-rewriteaof-bg-11132.aof\n11132:C 22 Jun 2026 03:59:08.249 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n15:M 22 Jun 2026 03:59:08.259 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 03:59:08.259 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-11132.aof into appendonly.aof.151.base.rdb\n15:M 22 Jun 2026 03:59:08.313 * Removing the history file appendonly.aof.150.incr.aof in the background\n15:M 22 Jun 2026 03:59:08.313 * Removing the history file appendonly.aof.150.base.rdb in the background\n15:M 22 Jun 2026 03:59:08.394 * Background AOF rewrite finished successfully\n2026/06/22 03:59:26 live trend gap at 11860699 after 11860679; rebuilding recent trend window\n2026/06/22 03:59:57 live trend gap at 11860732 after 11860679; rebuilding recent trend window\n2026/06/22 04:00:28 live trend gap at 11860771 after 11860752; rebuilding recent trend window\n2026/06/22 04:01:00 live trend gap at 11860817 after 11860752; rebuilding recent trend window\n2026/06/22 04:01:40 live trend gap at 11860865 after 11860840; rebuilding recent trend window\n2026/06/22 04:02:12 live trend gap at 11860915 after 11860840; rebuilding recent trend window\n2026/06/22 04:02:44 live trend gap at 11860954 after 11860916; rebuilding recent trend window\n2026/06/22 04:03:15 live trend gap at 11860991 after 11860916; rebuilding recent trend window\n15:M 22 Jun 2026 04:03:19.572 * Starting automatic rewriting of AOF on 1641% growth\n15:M 22 Jun 2026 04:03:19.575 * Creating AOF incr file appendonly.aof.152.incr.aof on background rewrite\n15:M 22 Jun 2026 04:03:19.577 * Background append only file rewriting started by pid 11307\n11307:C 22 Jun 2026 04:03:19.637 * Successfully created the temporary AOF base file temp-rewriteaof-bg-11307.aof\n11307:C 22 Jun 2026 04:03:19.638 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n15:M 22 Jun 2026 04:03:19.677 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 04:03:19.678 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-11307.aof into appendonly.aof.152.base.rdb\n15:M 22 Jun 2026 04:03:19.680 * Removing the history file appendonly.aof.151.incr.aof in the background\n15:M 22 Jun 2026 04:03:19.680 * Removing the history file appendonly.aof.151.base.rdb in the background\n15:M 22 Jun 2026 04:03:19.682 * Background AOF rewrite finished successfully\n2026/06/22 04:03:46 live trend gap at 11861030 after 11860997; rebuilding recent trend window\n2026/06/22 04:04:19 live trend gap at 11861075 after 11859394; rebuilding recent trend window\n2026/06/22 04:04:50 live trend gap at 11861108 after 11861074; rebuilding recent trend window\n2026/06/22 04:05:20 live trend gap at 11861142 after 11858308; rebuilding recent trend window\n2026/06/22 04:05:51 live trend gap at 11861182 after 11861140; rebuilding recent trend window\n2026/06/22 04:06:23 live trend gap at 11861226 after 11860433; rebuilding recent trend window\n2026/06/22 04:06:55 live trend gap at 11861266 after 11861217; rebuilding recent trend window\n15:M 22 Jun 2026 04:07:21.686 * Starting automatic rewriting of AOF on 1921% growth\n15:M 22 Jun 2026 04:07:21.692 * Creating AOF incr file appendonly.aof.153.incr.aof on background rewrite\n15:M 22 Jun 2026 04:07:21.693 * Background append only file rewriting started by pid 11476\n11476:C 22 Jun 2026 04:07:21.809 * Successfully created the temporary AOF base file temp-rewriteaof-bg-11476.aof\n11476:C 22 Jun 2026 04:07:21.810 * Fork CoW for AOF rewrite: current 1 MB, peak 1 MB, average 1 MB\n15:M 22 Jun 2026 04:07:21.895 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 04:07:21.895 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-11476.aof into appendonly.aof.153.base.rdb\n15:M 22 Jun 2026 04:07:21.898 * Removing the history file appendonly.aof.152.incr.aof in the background\n15:M 22 Jun 2026 04:07:21.898 * Removing the history file appendonly.aof.152.base.rdb in the background\n15:M 22 Jun 2026 04:07:21.906 * Background AOF rewrite finished successfully\n2026/06/22 04:07:26 live trend gap at 11861302 after 11860128; rebuilding recent trend window\n2026/06/22 04:08:01 live trend gap at 11861343 after 11861296; rebuilding recent trend window\n2026/06/22 04:08:31 live trend gap at 11861385 after 11861373; rebuilding recent trend window\n2026/06/22 04:09:01 live trend gap at 11861419 after 11861373; rebuilding recent trend window\n2026/06/22 04:09:22 global Redis window source height moved backward from 11861435 to 11799189; rebuilding bounded live window\n2026/06/22 04:10:20 chain websocket ingest stopped: read tcp 172.18.0.4:42584->172.17.0.1:18546: i/o timeout\n2026/06/22 04:10:35 chain websocket ingest stopped: read tcp 172.18.0.4:47300->172.17.0.1:18546: i/o timeout\n2026/06/22 04:10:50 chain websocket ingest stopped: read tcp 172.18.0.4:60506->172.17.0.1:18546: i/o timeout\n2026/06/22 04:11:05 chain websocket ingest stopped: read tcp 172.18.0.4:33246->172.17.0.1:18546: i/o timeout\n2026/06/22 04:11:22 global Redis window gap 81125 exceeds incremental cap 120; rebuilding bounded window at 11880380\n2026/06/22 04:11:22 stable chain head apply failed: eth_getBlockByNumber(0xb54565) returned null; eth_getBlockByNumber(0xb54568) returned null; eth_getBlockByNumber(0xb54566) returned null; eth_getBlockByNumber(0xb54567) returned null; eth_getBlockByNumber(0xb54569) returned null; eth_getBlockByNumber(0xb5456c) returned null; eth_getBlockByNumber(0xb5456a) returned null; eth_getBlockByNumber(0xb5456b) returned null; eth_getBlockByNumber(0xb5456d) returned null; eth_getBlockByNumber(0xb5456f) returned null; eth_getBlockByNumber(0xb5456e) returned null; eth_getBlockByNumber(0xb54571) returned null; eth_getBlockByNumber(0xb54570) returned null; eth_getBlockByNumber(0xb54572) returned null; eth_getBlockByNumber(0xb54575) returned null; eth_getBlockByNumber(0xb54574) returned null; eth_getBlockByNumber(0xb54573) returned null; eth_getBlockByNumber(0xb54579) returned null; eth_getBlockByNumber(0xb54578) returned null; eth_getBlockByNumber(0xb54576) returned null; eth_getBlockByNumber(0xb54577) returned null; eth_getBlockByNumber(0xb5457d) returned null; eth_getBlockByNumber(0xb5457b) returned null; eth_getBlockByNumber(0xb5457a) returned null; eth_getBlockByNumber(0xb5457c) returned null; eth_getBlockByNumber(0xb54580) returned null; eth_getBlockByNumber(0xb54581) returned null; eth_getBlockByNumber(0xb5457f) returned null; eth_getBlockByNumber(0xb5457e) returned null; eth_getBlockByNumber(0xb54584) returned null; eth_getBlockByNumber(0xb54585) returned null; eth_getBlockByNumber(0xb54583) returned null; eth_getBlockByNumber(0xb54582) returned null; eth_getBlockByNumber(0xb54588) returned null; eth_getBlockByNumber(0xb54587) returned null; eth_getBlockByNumber(0xb54586) returned null; eth_getBlockByNumber(0xb54589) returned null; eth_getBlockByNumber(0xb5458c) returned null; eth_getBlockByNumber(0xb5458b) returned null; eth_getBlockByNumber(0xb5458a) returned null; eth_getBlockByNumber(0xb5458d) returned null; eth_getBlockByNumber(0xb5458e) returned null; eth_getBlockByNumber(0xb54590) returned null; eth_getBlockByNumber(0xb54591) returned null; eth_getBlockByNumber(0xb5458f) returned null; eth_getBlockByNumber(0xb54592) returned null; eth_getBlockByNumber(0xb54595) returned null; eth_getBlockByNumber(0xb54593) returned null; eth_getBlockByNumber(0xb54594) returned null; eth_getBlockByNumber(0xb54599) returned null; eth_getBlockByNumber(0xb54598) returned null; eth_getBlockByNumber(0xb54596) returned null; eth_getBlockByNumber(0xb54597) returned null; eth_getBlockByNumber(0xb5459a) returned null; eth_getBlockByNumber(0xb5459c) returned null; eth_getBlockByNumber(0xb5459b) returned null; eth_getBlockByNumber(0xb5459d) returned null; eth_getBlockByNumber(0xb545a0) returned null; eth_getBlockByNumber(0xb5459f) returned null; eth_getBlockByNumber(0xb5459e) returned null; eth_getBlockByNumber(0xb545a3) returned null; eth_getBlockByNumber(0xb545a2) returned null; eth_getBlockByNumber(0xb545a1) returned null; eth_getBlockByNumber(0xb545a4) returned null; eth_getBlockByNumber(0xb545a8) returned null; eth_getBlockByNumber(0xb545a6) returned null; eth_getBlockByNumber(0xb545a7) returned null; eth_getBlockByNumber(0xb545a5) returned null; eth_getBlockByNumber(0xb545ac) returned null; eth_getBlockByNumber(0xb545ab) returned null; eth_getBlockByNumber(0xb545a9) returned null; eth_getBlockByNumber(0xb545aa) returned null; eth_getBlockByNumber(0xb545ad) returned null; eth_getBlockByNumber(0xb545b0) returned null; eth_getBlockByNumber(0xb545af) returned null; eth_getBlockByNumber(0xb545ae) returned null; eth_getBlockByNumber(0xb545b1) returned null; eth_getBlockByNumber(0xb545b2) returned null; eth_getBlockByNumber(0xb545b4) returned null; eth_getBlockByNumber(0xb545b5) returned null; eth_getBlockByNumber(0xb545b3) returned null; eth_getBlockByNumber(0xb545b6) returned null; eth_getBlockByNumber(0xb545b7) returned null; eth_getBlockByNumber(0xb545b9) returned null; eth_getBlockByNumber(0xb545ba) returned null; eth_getBlockByNumber(0xb545b8) returned null; eth_getBlockByNumber(0xb545bd) returned null; eth_getBlockByNumber(0xb545be) returned null; eth_getBlockByNumber(0xb545bc) returned null; eth_getBlockByNumber(0xb545bb) returned null; eth_getBlockByNumber(0xb545c1) returned null; eth_getBlockByNumber(0xb545c2) returned null; eth_getBlockByNumber(0xb545c0) returned null; eth_getBlockByNumber(0xb545bf) returned null; eth_getBlockByNumber(0xb545c4) returned null; eth_getBlockByNumber(0xb545c3) returned null; eth_getBlockByNumber(0xb545c5) returned null; eth_getBlockByNumber(0xb545c6) returned null; eth_getBlockByNumber(0xb545c7) returned null; eth_getBlockByNumber(0xb545c8) returned null; eth_getBlockByNumber(0xb545ca) returned null; eth_getBlockByNumber(0xb545cc) returned null; eth_getBlockByNumber(0xb545cb) returned null; eth_getBlockByNumber(0xb545c9) returned null; eth_getBlockByNumber(0xb545cd) returned null; eth_getBlockByNumber(0xb545cf) returned null; eth_getBlockByNumber(0xb545ce) returned null; eth_getBlockByNumber(0xb545d0) returned null; eth_getBlockByNumber(0xb545d2) returned null; eth_getBlockByNumber(0xb545d1) returned null; eth_getBlockByNumber(0xb545d3) returned null; eth_getBlockByNumber(0xb545d4) returned null; eth_getBlockByNumber(0xb545d7) returned null; eth_getBlockByNumber(0xb545d8) returned null; eth_getBlockByNumber(0xb545d5) returned null; eth_getBlockByNumber(0xb545d6) returned null; eth_getBlockByNumber(0xb545da) returned null; eth_getBlockByNumber(0xb545dc) returned null; eth_getBlockByNumber(0xb545d9) returned null; eth_getBlockByNumber(0xb545db) returned null; eth_getBlockByNumber(0xb545de) returned null; eth_getBlockByNumber(0xb545e0) returned null; eth_getBlockByNumber(0xb545df) returned null; eth_getBlockByNumber(0xb545dd) returned null; eth_getBlockByNumber(0xb545e1) returned null; eth_getBlockByNumber(0xb545e3) returned null; eth_getBlockByNumber(0xb545e2) returned null; eth_getBlockByNumber(0xb545e4) returned null; eth_getBlockByNumber(0xb545e5) returned null; eth_getBlockByNumber(0xb545e6) returned null; eth_getBlockByNumber(0xb545e7) returned null; eth_getBlockByNumber(0xb545e8) returned null; eth_getBlockByNumber(0xb545e9) returned null; eth_getBlockByNumber(0xb545ec) returned null; eth_getBlockByNumber(0xb545ea) returned null; eth_getBlockByNumber(0xb545eb) returned null; eth_getBlockByNumber(0xb545f0) returned null; eth_getBlockByNumber(0xb545ee) returned null; eth_getBlockByNumber(0xb545ed) returned null; eth_getBlockByNumber(0xb545f3) returned null; eth_getBlockByNumber(0xb545f1) returned null; eth_getBlockByNumber(0xb545f2) returned null; eth_getBlockByNumber(0xb545ef) returned null; eth_getBlockByNumber(0xb545f6) returned null; eth_getBlockByNumber(0xb545f7) returned null; eth_getBlockByNumber(0xb545f4) returned null; eth_getBlockByNumber(0xb545f5) returned null; eth_getBlockByNumber(0xb545fa) returned null; eth_getBlockByNumber(0xb545f8) returned null; eth_getBlockByNumber(0xb545f9) returned null; eth_getBlockByNumber(0xb545fb) returned null; eth_getBlockByNumber(0xb545fe) returned null; eth_getBlockByNumber(0xb545fc) returned null; eth_getBlockByNumber(0xb545fd) returned null; eth_getBlockByNumber(0xb54600) returned null; eth_getBlockByNumber(0xb545ff) returned null; eth_getBlockByNumber(0xb54601) returned null; eth_getBlockByNumber(0xb54602) returned null; eth_getBlockByNumber(0xb54603) returned null; eth_getBlockByNumber(0xb54604) returned null; eth_getBlockByNumber(0xb54605) returned null; eth_getBlockByNumber(0xb54607) returned null; eth_getBlockByNumber(0xb54606) returned null; eth_getBlockByNumber(0xb54608) returned null; eth_getBlockByNumber(0xb5460a) returned null; eth_getBlockByNumber(0xb54609) returned null; eth_getBlockByNumber(0xb5460c) returned null; eth_getBlockByNumber(0xb5460b) returned null; eth_getBlockByNumber(0xb5460e) returned null; eth_getBlockByNumber(0xb5460d) returned null; eth_getBlockByNumber(0xb54610) returned null; eth_getBlockByNumber(0xb5460f) returned null; eth_getBlockByNumber(0xb54614) returned null; eth_getBlockByNumber(0xb54613) returned null; eth_getBlockByNumber(0xb54611) returned null; eth_getBlockByNumber(0xb54612) returned null; eth_getBlockByNumber(0xb54616) returned null; eth_getBlockByNumber(0xb54618) returned null; eth_getBlockByNumber(0xb54615) returned null; eth_getBlockByNumber(0xb54617) returned null; eth_getBlockByNumber(0xb5461b) returned null; eth_getBlockByNumber(0xb54619) returned null; eth_getBlockByNumber(0xb5461c) returned null; eth_getBlockByNumber(0xb5461d) returned null; eth_getBlockByNumber(0xb5461a) returned null; eth_getBlockByNumber(0xb5461e) returned null; eth_getBlockByNumber(0xb54621) returned null; eth_getBlockByNumber(0xb54620) returned null; eth_getBlockByNumber(0xb5461f) returned null; eth_getBlockByNumber(0xb54622) returned null; eth_getBlockByNumber(0xb54625) returned null; eth_getBlockByNumber(0xb54623) returned null; eth_getBlockByNumber(0xb54624) returned null; eth_getBlockByNumber(0xb54628) returned null; eth_getBlockByNumber(0xb54629) returned null; eth_getBlockByNumber(0xb54626) returned null; eth_getBlockByNumber(0xb54627) returned null; eth_getBlockByNumber(0xb5462d) returned null; eth_getBlockByNumber(0xb5462c) returned null; eth_getBlockByNumber(0xb5462a) returned null; eth_getBlockByNumber(0xb5462b) returned null; eth_getBlockByNumber(0xb54631) returned null; eth_getBlockByNumber(0xb5462e) returned null; eth_getBlockByNumber(0xb54630) returned null; eth_getBlockByNumber(0xb5462f) returned null; eth_getBlockByNumber(0xb54633) returned null; eth_getBlockByNumber(0xb54634) returned null; eth_getBlockByNumber(0xb54632) returned null; eth_getBlockByNumber(0xb54635) returned null; eth_getBlockByNumber(0xb54638) returned null; eth_getBlockByNumber(0xb54637) returned null; eth_getBlockByNumber(0xb54639) returned null; eth_getBlockByNumber(0xb54636) returned null; eth_getBlockByNumber(0xb5463a) returned null; eth_getBlockByNumber(0xb5463b) returned null; eth_getBlockByNumber(0xb5463d) returned null; eth_getBlockByNumber(0xb5463c) returned null; eth_getBlockByNumber(0xb5463f) returned null; eth_getBlockByNumber(0xb54641) returned null; eth_getBlockByNumber(0xb5463e) returned null; eth_getBlockByNumber(0xb54640) returned null; eth_getBlockByNumber(0xb54643) returned null; eth_getBlockByNumber(0xb54644) returned null; eth_getBlockByNumber(0xb54645) returned null; eth_getBlockByNumber(0xb54642) returned null; eth_getBlockByNumber(0xb54646) returned null; eth_getBlockByNumber(0xb54647) returned null; eth_getBlockByNumber(0xb54648) returned null; eth_getBlockByNumber(0xb54649) returned null; eth_getBlockByNumber(0xb5464a) returned null; eth_getBlockByNumber(0xb5464c) returned null; eth_getBlockByNumber(0xb5464d) returned null; eth_getBlockByNumber(0xb5464b) returned null; eth_getBlockByNumber(0xb54650) returned null; eth_getBlockByNumber(0xb5464e) returned null; eth_getBlockByNumber(0xb54651) returned null; eth_getBlockByNumber(0xb5464f) returned null; eth_getBlockByNumber(0xb54652) returned null; eth_getBlockByNumber(0xb54654) returned null; eth_getBlockByNumber(0xb54653) returned null; eth_getBlockByNumber(0xb54655) returned null; eth_getBlockByNumber(0xb54658) returned null; eth_getBlockByNumber(0xb54657) returned null; eth_getBlockByNumber(0xb54656) returned null; eth_getBlockByNumber(0xb54659) returned null; eth_getBlockByNumber(0xb5465c) returned null; eth_getBlockByNumber(0xb5465b) returned null; eth_getBlockByNumber(0xb5465a) returned null; eth_getBlockByNumber(0xb5465d) returned null; eth_getBlockByNumber(0xb54661) returned null; eth_getBlockByNumber(0xb5465e) returned null; eth_getBlockByNumber(0xb54660) returned null; eth_getBlockByNumber(0xb5465f) returned null; eth_getBlockByNumber(0xb54662) returned null; eth_getBlockByNumber(0xb54665) returned null; eth_getBlockByNumber(0xb54663) returned null; eth_getBlockByNumber(0xb54664) returned null; eth_getBlockByNumber(0xb54667) returned null; eth_getBlockByNumber(0xb54669) returned null; eth_getBlockByNumber(0xb54668) returned null; eth_getBlockByNumber(0xb54666) returned null; eth_getBlockByNumber(0xb5466a) returned null; eth_getBlockByNumber(0xb5466c) returned null; eth_getBlockByNumber(0xb5466d) returned null; eth_getBlockByNumber(0xb5466b) returned null; eth_getBlockByNumber(0xb54671) returned null; eth_getBlockByNumber(0xb54670) returned null; eth_getBlockByNumber(0xb5466e) returned null; eth_getBlockByNumber(0xb5466f) returned null; eth_getBlockByNumber(0xb54675) returned null; eth_getBlockByNumber(0xb54674) returned null; eth_getBlockByNumber(0xb54672) returned null; eth_getBlockByNumber(0xb54673) returned null; eth_getBlockByNumber(0xb54676) returned null; eth_getBlockByNumber(0xb54677) returned null; eth_getBlockByNumber(0xb54679) returned null; eth_getBlockByNumber(0xb54678) returned null; eth_getBlockByNumber(0xb5467b) returned null; eth_getBlockByNumber(0xb5467a) returned null; eth_getBlockByNumber(0xb5467d) returned null; eth_getBlockByNumber(0xb5467c) returned null; eth_getBlockByNumber(0xb5467f) returned null; eth_getBlockByNumber(0xb5467e) returned null; eth_getBlockByNumber(0xb54681) returned null; eth_getBlockByNumber(0xb54680) returned null; eth_getBlockByNumber(0xb54683) returned null; eth_getBlockByNumber(0xb54682) returned null; eth_getBlockByNumber(0xb54685) returned null; eth_getBlockByNumber(0xb54684) returned null; eth_getBlockByNumber(0xb54686) returned null; eth_getBlockByNumber(0xb54689) returned null; eth_getBlockByNumber(0xb54687) returned null; eth_getBlockByNumber(0xb54688) returned null; eth_getBlockByNumber(0xb5468b) returned null; eth_getBlockByNumber(0xb5468c) returned null; eth_getBlockByNumber(0xb5468a) returned null; eth_getBlockByNumber(0xb5468d) returned null; eth_getBlockByNumber(0xb5468f) returned null; eth_getBlockByNumber(0xb54690) returned null; eth_getBlockByNumber(0xb5468e) returned null; eth_getBlockByNumber(0xb54691) returned null; eth_getBlockByNumber(0xb54694) returned null; eth_getBlockByNumber(0xb54693) returned null; eth_getBlockByNumber(0xb54695) returned null; eth_getBlockByNumber(0xb54692) returned null; eth_getBlockByNumber(0xb54698) returned null; eth_getBlockByNumber(0xb54699) returned null; eth_getBlockByNumber(0xb54697) returned null; eth_getBlockByNumber(0xb54696) returned null; eth_getBlockByNumber(0xb5469a) returned null; eth_getBlockByNumber(0xb5469d) returned null; eth_getBlockByNumber(0xb5469c) returned null; eth_getBlockByNumber(0xb5469f) returned null; eth_getBlockByNumber(0xb5469b) returned null; eth_getBlockByNumber(0xb5469e) returned null; eth_getBlockByNumber(0xb546a0) returned null; eth_getBlockByNumber(0xb546a3) returned null; eth_getBlockByNumber(0xb546a4) returned null; eth_getBlockByNumber(0xb546a1) returned null; eth_getBlockByNumber(0xb546a2) returned null; eth_getBlockByNumber(0xb546a5) returned null; eth_getBlockByNumber(0xb546a7) returned null; eth_getBlockByNumber(0xb546a6) returned null; eth_getBlockByNumber(0xb546ab) returned null; eth_getBlockByNumber(0xb546a8) returned null; eth_getBlockByNumber(0xb546aa) returned null; eth_getBlockByNumber(0xb546a9) returned null; eth_getBlockByNumber(0xb546af) returned null; eth_getBlockByNumber(0xb546ac) returned null; eth_getBlockByNumber(0xb546ad) returned null; eth_getBlockByNumber(0xb546ae) returned null; eth_getBlockByNumber(0xb546b0) returned null; eth_getBlockByNumber(0xb546b2) returned null; eth_getBlockByNumber(0xb546b3) returned null; eth_getBlockByNumber(0xb546b1) returned null; eth_getBlockByNumber(0xb546b6) returned null; eth_getBlockByNumber(0xb546b5) returned null; eth_getBlockByNumber(0xb546b4) returned null; eth_getBlockByNumber(0xb546b7) returned null; eth_getBlockByNumber(0xb546b9) returned null; eth_getBlockByNumber(0xb546bb) returned null; eth_getBlockByNumber(0xb546b8) returned null; eth_getBlockByNumber(0xb546ba) returned null; eth_getBlockByNumber(0xb546be) returned null; eth_getBlockByNumber(0xb546bd) returned null; eth_getBlockByNumber(0xb546bf) returned null; eth_getBlockByNumber(0xb546bc) returned null; eth_getBlockByNumber(0xb546c1) returned null; eth_getBlockByNumber(0xb546c3) returned null; eth_getBlockByNumber(0xb546c0) returned null; eth_getBlockByNumber(0xb546c2) returned null; eth_getBlockByNumber(0xb546c6) returned null; eth_getBlockByNumber(0xb546c7) returned null; eth_getBlockByNumber(0xb546c4) returned null; eth_getBlockByNumber(0xb546c5) returned null; eth_getBlockByNumber(0xb546cb) returned null; eth_getBlockByNumber(0xb546c8) returned null; eth_getBlockByNumber(0xb546c9) returned null; eth_getBlockByNumber(0xb546ca) returned null; eth_getBlockByNumber(0xb546cc) returned null; eth_getBlockByNumber(0xb546cf) returned null; eth_getBlockByNumber(0xb546ce) returned null; eth_getBlockByNumber(0xb546cd) returned null; eth_getBlockByNumber(0xb546d1) returned null; eth_getBlockByNumber(0xb546d3) returned null; eth_getBlockByNumber(0xb546d2) returned null; eth_getBlockByNumber(0xb546d0) returned null; eth_getBlockByNumber(0xb546d5) returned null; eth_getBlockByNumber(0xb546d7) returned null; eth_getBlockByNumber(0xb546d6) returned null; eth_getBlockByNumber(0xb546d4) returned null; eth_getBlockByNumber(0xb546da) returned null; eth_getBlockByNumber(0xb546d8) returned null; eth_getBlockByNumber(0xb546d9) returned null; eth_getBlockByNumber(0xb546db) returned null; eth_getBlockByNumber(0xb546dc) returned null; eth_getBlockByNumber(0xb546dd) returned null; eth_getBlockByNumber(0xb546de) returned null; eth_getBlockByNumber(0xb546e0) returned null; eth_getBlockByNumber(0xb546df) returned null; eth_getBlockByNumber(0xb546e2) returned null; eth_getBlockByNumber(0xb546e1) returned null; eth_getBlockByNumber(0xb546e3) returned null; eth_getBlockByNumber(0xb546e4) returned null; eth_getBlockByNumber(0xb546e7) returned null; eth_getBlockByNumber(0xb546e5) returned null; eth_getBlockByNumber(0xb546e6) returned null; eth_getBlockByNumber(0xb546e8) returned null; eth_getBlockByNumber(0xb546ec) returned null; eth_getBlockByNumber(0xb546ea) returned null; eth_getBlockByNumber(0xb546eb) returned null; eth_getBlockByNumber(0xb546e9) returned null; eth_getBlockByNumber(0xb546ee) returned null; eth_getBlockByNumber(0xb546ed) returned null; eth_getBlockByNumber(0xb546f0) returned null; eth_getBlockByNumber(0xb546ef) returned null; eth_getBlockByNumber(0xb546f4) returned null; eth_getBlockByNumber(0xb546f3) returned null; eth_getBlockByNumber(0xb546f1) returned null; eth_getBlockByNumber(0xb546f2) returned null; eth_getBlockByNumber(0xb546f5) returned null; eth_getBlockByNumber(0xb546f6) returned null; eth_getBlockByNumber(0xb546f7) returned null; eth_getBlockByNumber(0xb546f8) returned null; eth_getBlockByNumber(0xb546fa) returned null; eth_getBlockByNumber(0xb546f9) returned null; eth_getBlockByNumber(0xb546fc) returned null; eth_getBlockByNumber(0xb546fb) returned null; eth_getBlockByNumber(0xb546fe) returned null; eth_getBlockByNumber(0xb546fd) returned null; eth_getBlockByNumber(0xb54700) returned null; eth_getBlockByNumber(0xb546ff) returned null; eth_getBlockByNumber(0xb54701) returned null; eth_getBlockByNumber(0xb54702) returned null; eth_getBlockByNumber(0xb54704) returned null; eth_getBlockByNumber(0xb54703) returned null; eth_getBlockByNumber(0xb54708) returned null; eth_getBlockByNumber(0xb54707) returned null; eth_getBlockByNumber(0xb54706) returned null; eth_getBlockByNumber(0xb54705) returned null; eth_getBlockByNumber(0xb5470b) returned null; eth_getBlockByNumber(0xb54709) returned null; eth_getBlockByNumber(0xb5470c) returned null; eth_getBlockByNumber(0xb5470a) returned null; eth_getBlockByNumber(0xb5470e) returned null; eth_getBlockByNumber(0xb54710) returned null; eth_getBlockByNumber(0xb5470d) returned null; eth_getBlockByNumber(0xb5470f) returned null; eth_getBlockByNumber(0xb54712) returned null; eth_getBlockByNumber(0xb54713) returned null; eth_getBlockByNumber(0xb54714) returned null; eth_getBlockByNumber(0xb54711) returned null; eth_getBlockByNumber(0xb54715) returned null; eth_getBlockByNumber(0xb54716) returned null; eth_getBlockByNumber(0xb54717) returned null; eth_getBlockByNumber(0xb54718) returned null; eth_getBlockByNumber(0xb5471c) returned null; eth_getBlockByNumber(0xb5471b) returned null; eth_getBlockByNumber(0xb54719) returned null; eth_getBlockByNumber(0xb5471a) returned null; eth_getBlockByNumber(0xb5471d) returned null; eth_getBlockByNumber(0xb54720) returned null; eth_getBlockByNumber(0xb5471e) returned null; eth_getBlockByNumber(0xb5471f) returned null; eth_getBlockByNumber(0xb54722) returned null; eth_getBlockByNumber(0xb54721) returned null; eth_getBlockByNumber(0xb54723) returned null; eth_getBlockByNumber(0xb54724) returned null; eth_getBlockByNumber(0xb54726) returned null; eth_getBlockByNumber(0xb54727) returned null; eth_getBlockByNumber(0xb54728) returned null; eth_getBlockByNumber(0xb54725) returned null; eth_getBlockByNumber(0xb5472c) returned null; eth_getBlockByNumber(0xb5472b) returned null; eth_getBlockByNumber(0xb54729) returned null; eth_getBlockByNumber(0xb5472a) returned null; eth_getBlockByNumber(0xb5472d) returned null; eth_getBlockByNumber(0xb54730) returned null; eth_getBlockByNumber(0xb5472f) returned null; eth_getBlockByNumber(0xb5472e) returned null; eth_getBlockByNumber(0xb54731) returned null; eth_getBlockByNumber(0xb54734) returned null; eth_getBlockByNumber(0xb54732) returned null; eth_getBlockByNumber(0xb54733) returned null; eth_getBlockByNumber(0xb54736) returned null; eth_getBlockByNumber(0xb54737) returned null; eth_getBlockByNumber(0xb54738) returned null; eth_getBlockByNumber(0xb54735) returned null; eth_getBlockByNumber(0xb54739) returned null; eth_getBlockByNumber(0xb5473a) returned null; eth_getBlockByNumber(0xb5473d) returned null; eth_getBlockByNumber(0xb5473c) returned null; eth_getBlockByNumber(0xb5473b) returned null; eth_getBlockByNumber(0xb54740) returned null; eth_getBlockByNumber(0xb5473f) returned null; eth_getBlockByNumber(0xb5473e) returned null; eth_getBlockByNumber(0xb54741) returned null; eth_getBlockByNumber(0xb54742) returned null; eth_getBlockByNumber(0xb54744) returned null; eth_getBlockByNumber(0xb54743) returned null; eth_getBlockByNumber(0xb54745) returned null; eth_getBlockByNumber(0xb54748) returned null; eth_getBlockByNumber(0xb54747) returned null; eth_getBlockByNumber(0xb54749) returned null; eth_getBlockByNumber(0xb54746) returned null; eth_getBlockByNumber(0xb5474a) returned null; eth_getBlockByNumber(0xb5474b) returned null; eth_getBlockByNumber(0xb5474d) returned null; eth_getBlockByNumber(0xb5474c) returned null; eth_getBlockByNumber(0xb54750) returned null; eth_getBlockByNumber(0xb54751) returned null; eth_getBlockByNumber(0xb5474f) returned null; eth_getBlockByNumber(0xb5474e) returned null; eth_getBlockByNumber(0xb54752) returned null; eth_getBlockByNumber(0xb54754) returned null; eth_getBlockByNumber(0xb54753) returned null; eth_getBlockByNumber(0xb54755) returned null; eth_getBlockByNumber(0xb54756) returned null; eth_getBlockByNumber(0xb54759) returned null; eth_getBlockByNumber(0xb54758) returned null; eth_getBlockByNumber(0xb54757) returned null; eth_getBlockByNumber(0xb5475a) returned null; eth_getBlockByNumber(0xb5475c) returned null; eth_getBlockByNumber(0xb5475d) returned null; eth_getBlockByNumber(0xb5475b) returned null; eth_getBlockByNumber(0xb54761) returned null; eth_getBlockByNumber(0xb5475f) returned null; eth_getBlockByNumber(0xb5475e) returned null; eth_getBlockByNumber(0xb54760) returned null; eth_getBlockByNumber(0xb54762) returned null; eth_getBlockByNumber(0xb54763) returned null; eth_getBlockByNumber(0xb54764) returned null; eth_getBlockByNumber(0xb54765) returned null; eth_getBlockByNumber(0xb54769) returned null; eth_getBlockByNumber(0xb54768) returned null; eth_getBlockByNumber(0xb54767) returned null; eth_getBlockByNumber(0xb54766) returned null; eth_getBlockByNumber(0xb5476c) returned null; eth_getBlockByNumber(0xb5476d) returned null; eth_getBlockByNumber(0xb5476b) returned null; eth_getBlockByNumber(0xb5476a) returned null; eth_getBlockByNumber(0xb54771) returned null; eth_getBlockByNumber(0xb54770) returned null; eth_getBlockByNumber(0xb5476f) returned null; eth_getBlockByNumber(0xb5476e) returned null; eth_getBlockByNumber(0xb54775) returned null; eth_getBlockByNumber(0xb54773) returned null; eth_getBlockByNumber(0xb54772) returned null; eth_getBlockByNumber(0xb54774) returned null; eth_getBlockByNumber(0xb54777) returned null; eth_getBlockByNumber(0xb54776) returned null; eth_getBlockByNumber(0xb54778) returned null; eth_getBlockByNumber(0xb54779) returned null; eth_getBlockByNumber(0xb5477c) returned null; eth_getBlockByNumber(0xb5477b) returned null; eth_getBlockByNumber(0xb5477d) returned null; eth_getBlockByNumber(0xb5477a) returned null; eth_getBlockByNumber(0xb5477f) returned null; eth_getBlockByNumber(0xb5477e) returned null; eth_getBlockByNumber(0xb54780) returned null; eth_getBlockByNumber(0xb54781) returned null; eth_getBlockByNumber(0xb54785) returned null; eth_getBlockByNumber(0xb54782) returned null; eth_getBlockByNumber(0xb54783) returned null; eth_getBlockByNumber(0xb54784) returned null; eth_getBlockByNumber(0xb54786) returned null; eth_getBlockByNumber(0xb54788) returned null; eth_getBlockByNumber(0xb54789) returned null; eth_getBlockByNumber(0xb54787) returned null; eth_getBlockByNumber(0xb5478c) returned null; eth_getBlockByNumber(0xb5478b) returned null; eth_getBlockByNumber(0xb5478a) returned null; eth_getBlockByNumber(0xb5478d) returned null; eth_getBlockByNumber(0xb5478e) returned null; eth_getBlockByNumber(0xb54791) returned null; eth_getBlockByNumber(0xb54790) returned null; eth_getBlockByNumber(0xb5478f) returned null; eth_getBlockByNumber(0xb54795) returned null; eth_getBlockByNumber(0xb54793) returned null; eth_getBlockByNumber(0xb54794) returned null; eth_getBlockByNumber(0xb54792) returned null; eth_getBlockByNumber(0xb54798) returned null; eth_getBlockByNumber(0xb54799) returned null; eth_getBlockByNumber(0xb54797) returned null; eth_getBlockByNumber(0xb54796) returned null; eth_getBlockByNumber(0xb5479b) returned null; eth_getBlockByNumber(0xb5479a) returned null; eth_getBlockByNumber(0xb5479d) returned null; eth_getBlockByNumber(0xb5479c) returned null; eth_getBlockByNumber(0xb547a1) returned null; eth_getBlockByNumber(0xb5479e) returned null; eth_getBlockByNumber(0xb5479f) returned null; eth_getBlockByNumber(0xb547a0) returned null; eth_getBlockByNumber(0xb547a3) returned null; eth_getBlockByNumber(0xb547a2) returned null; eth_getBlockByNumber(0xb547a4) returned null; eth_getBlockByNumber(0xb547a5) returned null; eth_getBlockByNumber(0xb547a7) returned null; eth_getBlockByNumber(0xb547a9) returned null; eth_getBlockByNumber(0xb547a6) returned null; eth_getBlockByNumber(0xb547a8) returned null; eth_getBlockByNumber(0xb547ab) returned null; eth_getBlockByNumber(0xb547aa) returned null; eth_getBlockByNumber(0xb547ad) returned null; eth_getBlockByNumber(0xb547ac) returned null; eth_getBlockByNumber(0xb547b0) returned null; eth_getBlockByNumber(0xb547b1) returned null; eth_getBlockByNumber(0xb547af) returned null; eth_getBlockByNumber(0xb547ae) returned null; eth_getBlockByNumber(0xb547b5) returned null; eth_getBlockByNumber(0xb547b3) returned null; eth_getBlockByNumber(0xb547b4) returned null; eth_getBlockByNumber(0xb547b2) returned null; eth_getBlockByNumber(0xb547b9) returned null; eth_getBlockByNumber(0xb547b8) returned null; eth_getBlockByNumber(0xb547b6) returned null; eth_getBlockByNumber(0xb547b7) returned null; eth_getBlockByNumber(0xb547ba) returned null; eth_getBlockByNumber(0xb547bc) returned null; eth_getBlockByNumber(0xb547bb) returned null\n2026/06/22 04:11:22 global Redis window gap 62258 exceeds incremental cap 120; rebuilding bounded window at 11861513\n2026/06/22 04:11:25 live trend gap at 11861514 after 11861373; rebuilding recent trend window\n2026/06/22 04:11:56 live trend gap at 11861547 after 11861515; rebuilding recent trend window\n2026/06/22 04:12:30 live trend gap at 11861607 after 11859030; rebuilding recent trend window\n15:M 22 Jun 2026 04:12:35.919 * Starting automatic rewriting of AOF on 1655% growth\n15:M 22 Jun 2026 04:12:35.923 * Creating AOF incr file appendonly.aof.154.incr.aof on background rewrite\n15:M 22 Jun 2026 04:12:35.924 * Background append only file rewriting started by pid 11694\n11694:C 22 Jun 2026 04:12:36.006 * Successfully created the temporary AOF base file temp-rewriteaof-bg-11694.aof\n11694:C 22 Jun 2026 04:12:36.009 * Fork CoW for AOF rewrite: current 2 MB, peak 2 MB, average 1 MB\n15:M 22 Jun 2026 04:12:36.026 * Background AOF rewrite terminated with success\n15:M 22 Jun 2026 04:12:36.026 * Successfully renamed the temporary AOF base file temp-rewriteaof-bg-11694.aof into appendonly.aof.154.base.rdb\n15:M 22 Jun 2026 04:12:36.034 * Removing the history file appendonly.aof.153.incr.aof in the background\n15:M 22 Jun 2026 04:12:36.034 * Removing the history file appendonly.aof.153.base.rdb in the background\n15:M 22 Jun 2026 04:12:36.035 * Background AOF rewrite finished successfully\n2026/06/22 04:13:00 live trend gap at 11861657 after 11861606; rebuilding recent trend window\n2026/06/22 04:13:33 live trend gap at 11861718 after 11859010; rebuilding recent trend window\n2026/06/22 04:14:05 live trend gap at 11861773 after 11861714; rebuilding recent trend window\n2026/06/22 04:14:37 live trend gap at 11861819 after 11859747; rebuilding recent trend window\n2026/06/22 04:15:09 live trend gap at 11861861 after 11861811; rebuilding recent trend window\n2026/06/22 04:15:40 live trend gap at 11861902 after 11859707; rebuilding recent trend window", + "ports": "8088/tcp -> 127.0.0.1:8088" + }, + "node": { + "image_latest_id": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "image_safepoint_id": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "/usr/local/bin/nodeworker", + "--node-binary=/usr/local/bin/blockdag-node", + "--node-args=--configfile /etc/bdagStack/node.conf", + "--rpc-url=ws://127.0.0.1:18546", + "--dag-rpc-url=http://127.0.0.1:38131", + "--persist-root=/var/lib/bdagStack/nodeworker", + "--health-min-peers=2", + "--rollout-window=30m" + ], + "Env": [ + "BDAG_ENTRYPOINT_CHOWN_MODE=needed", + "BDAG_NODE_MINING_NO_PENDING_TX=0", + "TEMP=/tmp", + "BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "TMP=/tmp", + "BDAG_NODE_MODULES=Blockdag,miner", + "MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m", + "BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc", + "SYNC_SOURCE_NODE=0", + "BDAG_NODE_ARCHIVAL=0", + "BDAG_SNAPSHOT_URL=", + "P2P_PORT=8155", + "BDAG_STORAGE_PROFILE=auto", + "BDAG_DETECTED_NETWORK_TOPOLOGY=", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "BDAG_ENABLE_NODE_MINING=1", + "BDAG_NODE_NO_FILE_LOGGING=1", + "BDAG_NETWORK_TOPOLOGY=auto", + "BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP", + "BDAG_P2P_ADVERTISE_IP=", + "BDAG_P2P_INTERFACE=", + "BDAG_NODE_MINING_ARGS=", + "TMPDIR=/tmp", + "BDAG_NODE_OBSOLETE_HEIGHT=20", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "BDAG_NODE_DEBUG_LEVEL=debug", + "BDAG_NETWORK=", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "18545/tcp": {}, + "18546/tcp": {}, + "38131/tcp": {}, + "38132/tcp": {}, + "6060/tcp": {}, + "8150/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-node", + "Labels": { + "com.docker.compose.config-hash": "cd2bed17cbd5f264367a3668eb1187c214cd03132542f8169834b50d2038d943", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "node", + "com.docker.compose.service": "node", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/node" + }, + "Created": "2026-06-21T22:27:14.249227789Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/home/jeremy/blockdag-source/stack-redis/node.conf:/etc/bdagStack/node.conf:ro", + "/home/jeremy/blockdag-source/stack/data/node:/var/lib/bdagStack/node:rw", + "stack_nodeworker-data:/var/lib/bdagStack/nodeworker:rw", + "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker:/snapshot/latest.bdsnap:ro" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 4096, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -900, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 536870912, + "Tmpfs": { + "/run/bdag-ephemeral": "size=512m,mode=1777", + "/tmp": "size=512m,mode=1777", + "/var/tmp": "size=512m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hostname", + "HostsPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/hosts", + "Id": "98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e", + "Image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "ImageManifestDescriptor": { + "digest": "sha256:7ffcf30b8a85bad3b77a4437afbd2c45d249c87a1fed5f0b6203dd2a6b5ffc6e", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2564 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/etc/bdagStack/node.conf", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/node.conf", + "Type": "bind" + }, + { + "Destination": "/var/lib/bdagStack/node", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack/data/node", + "Type": "bind" + }, + { + "Destination": "/var/lib/bdagStack/nodeworker", + "Driver": "local", + "Mode": "rw", + "Name": "stack_nodeworker-data", + "Propagation": "", + "RW": true, + "Source": "/var/lib/docker/volumes/stack_nodeworker-data/_data", + "Type": "volume" + }, + { + "Destination": "/snapshot/latest.bdsnap", + "Mode": "ro", + "Propagation": "rprivate", + "RW": false, + "Source": "/home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker", + "Type": "bind" + } + ], + "Name": "/node", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "6a9346674bfadb8c19a03ff52c78803f32a10c9ad091efa7164f99827c3e3fb2", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "747be80cabc6764f4fc40a1e1dcdd2f581cbcbb8c329b64bf5e46e74c31dd0d2", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/docker-entrypoint-nodeworker.sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/98dd87af9707037af1c7f9958c7b09aa3f51bb1de3bb6c6df25d5526fe21de9e/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3534321, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T22:27:16.468444346Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2e70e9..7f0e76 from=0xC0c4735662e5b7E02E7B72C15948640A40120aaF err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=0acb0c..8559bf from=0x24Ed96109607C4FD2cdE58524B1e822c3650784E err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8e3e83..721e56 from=0x2f0fBFE9F16E78aaF878C22bc61287B8aBadb9f2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ab11a1..485104 from=0xAa8f46eD2b609D293a3850103bfe60e4165FEdc7 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=794055..8703c7 from=0x7cFe5f1Bf78DaE3CD18eaDD1bc92925703dF74a0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f28f9f..a5c8be from=0x39c10d70bc363E2552aBEe500C113775e8D90C85 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1491d7..c6cd01 from=0x9E41245AE3aF2952a2208Df99326F5c9A5F15c67 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3a5728..7cd89d from=0xDF0Ae9796DC1d6B59faDD144089FaD349F3b6dB9 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2702ba..6f002f from=0xfa195a9C9654563389E0246960580761e679EAC4 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=92dc72..9c365a from=0x205C61B5C4A47b9d341863d817ebA5369D4bed15 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=82708c..4713a8 from=0x5eACc0f51cC6387875bE1FC8dBa707c5BB5b4026 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f58d8f..3b6cff from=0x718b1890A074B4A730326eD63D6aF16FaDD88C28 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=d1cad9..a3dadd from=0xd6d074268AC528aAA80BE7cBA0c4aeE7eDE95ea9 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=297791..94aa43 from=0x50096E9cd55a3eF50D1103AFB926FFcD5c83Fea0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=0b3c45..57b0ff from=0xd437383F0BaE24165b58e679B228067b4D8Ea2Fe err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b01ab6..78018a from=0xe0A259751E549abE601A85E74f1beE46f1D39BB0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9825d7..350a37 from=0x329c367BdfcF80c296af1Ed63C3Ae590Fc311508 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1e3369..e4a1a6 from=0x81Dd9d105924c3beB1610145cd21f2C712257067 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1b5e84..34b25b from=0xF42e6Bc68ECA5AD7E719d8876AE544282c7458d2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f8acf1..022283 from=0xbAadc186AD1b0935b523C3D04ee52868b4641F1b err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c23c4c..c37a56 from=0x448B68c13863589FbE322DCCC986557fBFf8a440 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ecb559..01bad6 from=0xfaef2FE87B66B9FAee730D6a2158109303Bb08B3 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2057ec..33b58b from=0x9b9f6Bd6A84ddB75630667460BF9740f187Cf77c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5d9adb..0d9775 from=0x864806c30992b7B24C2BFDF79a35293A9f5c8111 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=fa1878..5f6c80 from=0x564a13E53Dbb07DFE90Aa21944B854f8693e2356 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7f877c..827237 from=0x83fd5B24fE8350db41E7BABd49bD95563Bfb88a7 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=772c15..2366db from=0xE59DD6c972f1aF0bB48Fd14CEF99C8ee62a708E1 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b883cc..f83554 from=0x92277561655C2E833F78f848C1c904522122f447 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c3cd9c..5446a5 from=0xC0d192dd025eF2730Ab6Ec5A7299A8C98329e611 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=756948..0b06c6 from=0x5941d1732a6550bea57D5090FA87BEc3582Dc44c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=bb21b1..1c07be from=0xE254bc431a364417B02a8cf3a302cA72928E869F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8b3caf..c8b44f from=0x2e19646b84d2A383eA9d4b51B868531c34E8B8fc err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6f276c..8a8183 from=0x562D25D81330B656ac9E410261fcA5Fe9BcBa6A5 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4a2cf7..29b7fc from=0x157493649C0132776E1595c7e6F5D98B583aa236 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=bbb685..004f3b from=0x6ACCf2BaEDA04688AfeD21b544fF1678da611Bbb err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c65cd0..a4c6cd from=0x03D6DC13aDA868109A00947608d1210Ceb7F1662 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5b9ab9..572a02 from=0x8De511c44b8A77eBd84488E5032EFeb595B4dE28 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=07dbb1..26c61b from=0x108bA846eeaAEd447E2c69918e3665dcaaC2Cdd1 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a82815..7fa538 from=0xcEb35B92792EA4D54D7f78384767fFD217477111 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=036f85..65c020 from=0x6DdeAA69A0d7C88d0B9ac77A172A8868f49700cB err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8ddea9..065249 from=0x563A50AE04adFAe6c7949649376381cf2032b8cC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=2ea66c..454f4e from=0xbBe7AB304c479DA50A3e669642e1b24f81f83d21 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4f8eb5..57dfd9 from=0x69fB77B198F3e136fb6b563a0291a2183cAEAeDe err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=236468..6fca3a from=0x32C35d0bB937474B1D5765B088466eF50E12d10d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=344c77..2d2c6e from=0xb1335B7b9b4E5d9B49Fd32dE0E64450851ED92E2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5c5041..bffca0 from=0x5F0e335a28a8E41d47b4CB4c8cc7Ed44A49b7AfF err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9267f3..9e3d2f from=0xaA36CF946C24d7f6Aa03C5215166cA15Fb101134 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=c162dc..466147 from=0x8F9c3f1113312e3d72ED191A03fF1bc461b76240 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=e82c00..6af033 from=0xEdb0a6d0636fc1Ff7e09c2ea2E76DAB400c1790C err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6901dc..93f88b from=0x2bFAb2358e406295A8597B3B09B6EcB178A29110 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6c48f3..665b4d from=0x357Cd083b70a5c46d8E5C66bF472A0Bc1f23D1F8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6b2f14..6a936c from=0x8C4a200C68ce41aC785cDD4191cDA5B7b7F92D06 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.724 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=d56977..72846a from=0x3bcfBb999f0A5e7835A6EDE3F081B4f9d4Eb178d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=16e773..4ebb98 from=0x73Be4d40478156621A2C521282A4e7aAc09636dC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8f394b..0a744a from=0x3953218fE81EfbcB23728B82FC884B526738543b err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=211a2f..f93d69 from=0x7CD9557b07ba49e488D092FbbD963D581005434d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f0aff4..31c48a from=0x5F0c89A367876143e216610771A946C0D9Ed21Cd err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=76afe3..ab317f from=0x36d9eCd641197bc32B37Cc5e20514CeaaC9E5101 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=52fd86..1d6e70 from=0x8FEB33ac99F4B63d169329842E5471a8D5B77b99 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a83cf8..a3be4f from=0xCAc7Ed66ab72fcE5Cb96744C68B967CF72ab562F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8cccd1..f4ff0a from=0xba17caB7C18B908820F1458855372b39753E4672 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=85d24d..bff599 from=0xc3053bC26763cC44F0431aF4be5B25156A5e2DC0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=8a746b..c2eff7 from=0x25D98A6f653d969a0F4A1F13B63355426fB93235 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9d3a66..c581bb from=0x06ab4D265974BD0e4c48d2dD713EdDfEecD64FA2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=96d63a..7116c8 from=0x312C090a849FdeE8D5cC0f34F2a1cB34955a9092 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=f2f483..0266f4 from=0x2daBc83A51a312a839c16574f4E27AA68CB8A8C8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1a9055..b89cd9 from=0x83E9C2DC58E8dcd7559CB3630d0Ad24Bec51b13c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ec1eae..c18568 from=0xb8485d6693AB4fF7BD403868208f888Fc4dE656f err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7da959..cc3ba3 from=0xAfaFd70c3d150718FF69A27Dac891fE41fEb47f5 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=53e26a..7e1630 from=0x86fbA9C0b0C7b0FD7c9EB9fe6A1b991329690a3F err=\"address is blocked by on-chain governance blocklist: recipient 0xF1c4393Fc08A965034063D637E3C965048A0e5f2\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4acce7..cf838f from=0x08b68908cE4a389Ed10EDf6e149eA2eae1Ba4968 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=475d14..873f91 from=0x96A4d857c46D1e8c23E1a002189acf530aC128f7 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=4ba395..4959c7 from=0xD3A58bDB6C80E2B331a3f89811ea4B642be32446 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a9f3f6..f6b49f from=0x73EC36279f9A774DAabf204AE4d97F827a4DfCA8 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ffb6dd..bca92d from=0x6CE262412f436A0a8246FA2Cf70DAE779B218126 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a41ff1..a5fc9b from=0xcF65d023346C10f8CA6B097B3479A532476b5645 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6a4e3d..99bbb8 from=0x9252657c7db9f116586a75E95D788545D96A38EF err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b05100..d1c3aa from=0x9F1158F81E2EaDc0Ce0D490eE64703207DA9dE3e err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ab6640..14d043 from=0x20d7D512aC4C885b12145f47c653fb5bd1f1B3dC err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=493e91..23c3d4 from=0xc2e0E98Cb12F919ae1206faAc2C5a856aE6c7Cb2 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a2c1b5..1d766a from=0x769656a9A24483bEb75d85574130DF6490853687 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=07a2ca..cd2c27 from=0xAC02a613C816d53e97331ec8E6c11eC787199A7d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a83d2c..b7a451 from=0x6FF39356A422CE91e278F0aB024DCb81D842fd93 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=ac17c0..102fe0 from=0x4a2b631957c48e9E3b6A50d623e0A9E16325b453 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=a88727..c56671 from=0xb67dBCE5f5f2406cE10eD589126c25cC899d3a91 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=9f7f85..e4ff3c from=0x53995A59AaeA1025f0297159c12Ec5d14166347d err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5d5aba..638ca0 from=0xdA46521A42D0fF5bc55fb75A8109BF8D4075008c err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=bad675..43492a from=0x79C2d75E8748193b99aF46294F2d6eF5886d0bE0 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1b0ec4..1bb2a8 from=0xA6Bc27Dd5C3b5f67a3f8272854E37Cc79Bf66138 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=b14e0e..d4899e from=0xbE51a3eaD791FAd33C6D8D648529b559Aed54597 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=e1dd19..16fa85 from=0x4813d5f4792B85b33BB8b58BA1e74c39e7A07976 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=04ae0a..dce647 from=0x2bfcB88ad3f71b6279d2ee0eFa4B215A9e5B44aA err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=6d514b..7efba1 from=0x958ed7317d7a12D9211d29c60077B3f14EC76837 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=3cb531..dcfc5c from=0xC0c7082379E68004A9746CE3a5F1768ec8DEd33F err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=5dd78b..5fae00 from=0x8fc52379b14a43CB9cAD764D93627D7DDaa30795 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=1ae958..f91920 from=0x157f687727A7cDE9c674dEb7e5b2b047ABE51Ec4 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=7e8293..c113ac from=0x93b2E311805D6c6C4A83b10D4CB4302ae79663Fb err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.725 [DEBUG] Skipping governance-inadmissible transaction in template module=BDAG hash=74bd26..3e1555 from=0xB64F7Eb749166696A27182dFa48B1806Bc98Ce99 err=\"address is blocked by on-chain governance blocklist: recipient 0x558999486D976eC2Ddc18Da9a97c7Ccb78d9BD18\"\n2026-06-22|04:15:44.726 [DEBUG] Update BdagPool module=BDAG number=11861917 txs=1 gas=21000 fees=2.1e-08 elapsed=4.080ms\n2026-06-22|04:15:44.726 [INFO ] update BdagPool snapshot module=BDAG size=1\n2026-06-22|04:15:44.726 [DEBUG] Auto-staked 7330380572400000000 BDAG (minted 374670000967106 Shares) for 0x1719E0ee598c15957448D5E568948101DF78e7A0 in Epoch 19. module=bdag/consensus\n2026-06-22|04:15:44.727 [DEBUG] gbtdetail module=miner txcount=1 parents=\"[{Data:f59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8 Hash:0xf59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8}]\" prehash=0xf59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8 longpollid=0xf59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8-1782101744\n2026-06-22|04:15:44.727 [DEBUG] gbtend module=miner txcount=1 longpollid=0xf59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8-1782101744 spent=0\n2026-06-22|04:15:44.727 [DEBUG] End RPC Call (id:&1 method:Blockdag_GetBlockTemplate) module=RPC\n2026-06-22|04:15:44.727 [DEBUG] request handled module=gateway method=getBlockTemplate target= duration=5.506333ms\n2026-06-22|04:15:44.737 [DEBUG] Served eth_getBalance \u001b[0mconn\u001b[0m=127.0.0.1:53980 \u001b[0mreqid\u001b[0m=23222 \u001b[0mduration\u001b[0m=\"104.432\u00b5s\"\n2026-06-22|04:15:44.738 [DEBUG] request handled module=gateway method=eth_getBalance target= duration=259.377\u00b5s\n2026-06-22|04:15:44.738 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11861916 maxHeight=11880763\n2026-06-22|04:15:44.738 [DEBUG] using sender affinity for nonce module=gateway from=0x1719e0ee598c15957448d5e568948101df78e7a0 key=\n2026-06-22|04:15:44.738 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545 host=192.168.1.103 height=11861887 latency=9.684222ms synced=true active=true\n2026-06-22|04:15:44.738 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545\n2026-06-22|04:15:44.742 [DEBUG] request handled module=gateway method=eth_getTransactionCount target=192.168.1.103:18545 duration=4.383019ms\n2026-06-22|04:15:44.743 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11861916 maxHeight=11880763\n2026-06-22|04:15:44.743 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545 host=192.168.1.103 height=11861887 latency=9.684222ms synced=true active=true\n2026-06-22|04:15:44.743 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545\n2026-06-22|04:15:44.757 [DEBUG] request handled module=gateway method=eth_gasPrice target=192.168.1.103:18545 duration=14.564011ms\n2026-06-22|04:15:44.758 [DEBUG] Node is current but behind best peer, using best peer module=gateway localHeight=11861916 maxHeight=11880763\n2026-06-22|04:15:44.758 [DEBUG] using sender affinity module=gateway from=0x1719E0ee598c15957448D5E568948101DF78e7A0 key=\n2026-06-22|04:15:44.758 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545 host=192.168.1.103 height=11861887 latency=9.684222ms synced=true active=true\n2026-06-22|04:15:44.758 [DEBUG] using best peer module=gateway target=http://192.168.1.103:18545", + "ports": "" + }, + "pool": { + "image_latest_id": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "image_safepoint_id": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/usr/local/bin/mining-pool" + ], + "Env": [ + "POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30", + "POOL_BLOCK_TIMING_CONTROLLER_ENABLED=true", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "POOL_INSTANCE_ID=", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=0", + "POOL_SUBMIT_RPC_URLS=", + "POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true", + "POOL_PRIVATE_KEY=", + "POOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com", + "POOL_STALE_RACE_REJECT_WINDOW_SECONDS=10", + "POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5", + "POOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a", + "POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS=60", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES=20", + "TMP=/tmp", + "POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15", + "POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20", + "POOL_SUBMIT_STALE_BLOCK_CANDIDATES=false", + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30", + "POOL_TEMPLATE_TTL_REFRESH_MS=100", + "POOL_RPC_BACKENDS=", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750", + "POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS=20", + "BDAG_ASIC_LAN_CIDRS=192.168.1.0/24", + "TMPDIR=/tmp", + "POOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a", + "WALLET_RPC_URL=http://127.0.0.1:18545", + "POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS=60", + "POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT=5", + "PG_URL=postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable", + "MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_BIND_ADDR=192.168.1.100:3334", + "POOL_BLOCK_MATURITY=10", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100", + "POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3", + "POOL_RUNTIME_ADMIN_ENABLED=false", + "POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=15", + "POOL_PAYOUT_MAX_BLOCKS_PER_TICK=5", + "POOL_BLOCK_TIMING_CONTROLLER_STEP_MS=250", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=1000", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750", + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0", + "POOL_PAYOUT_MATURITY=4096", + "POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true", + "POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS=60", + "TEMP=/tmp", + "NODE_RPC_USER=test", + "NODE_RPC_URLS=http://127.0.0.1:38131", + "METRICS_ADDR=0.0.0.0:9090", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=false", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750", + "POOL_FEE_PERCENTAGE=1.0", + "POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS=120", + "NODE_RPC_URL=http://127.0.0.1:38131", + "NODE_RPC_PASS=", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=true", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=8000", + "PPLNS_N_WORK=1000", + "POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "ExposedPorts": { + "3334/tcp": {}, + "8080/tcp": {} + }, + "Hostname": "asus-miner", + "Image": "stack-pool", + "Labels": { + "com.docker.compose.config-hash": "3cc1bff3269cba209e027936c6f19ed844abc881d2e10bd0111807726ea628d1", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "pool", + "com.docker.compose.service": "pool", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.version": "24.04" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "bdagStack", + "Volumes": null, + "WorkingDir": "/var/lib/bdagStack/pool" + }, + "Created": "2026-06-21T23:31:44.333651017Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 3072, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [], + "GroupAdd": null, + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "host", + "OomKillDisable": null, + "OomScoreAdj": -800, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777", + "/var/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/hostname", + "HostsPath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/hosts", + "Id": "af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d", + "Image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "ImageManifestDescriptor": { + "digest": "sha256:650c2c542fd237362f559231bc8af2b7cce67dbef9b45a8e7775724cf5f539ed", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2000 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [], + "Name": "/pool", + "NetworkSettings": { + "Networks": { + "host": { + "Aliases": null, + "DNSNames": null, + "DriverOpts": null, + "EndpointID": "368bdc5ba2b6dc822d00519c5907e1128f7a12b3c35dfc97aba9cb1a83261de3", + "Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "", + "NetworkID": "bfa626d2f83edda27bd53326917f61333b7f9e9821a1544f60919c85ca0e29cf" + } + }, + "Ports": {}, + "SandboxID": "d32ed26490b4ca57aa26dbd57bd1c13e39623c9315a68b7acf8007a4e6f3f9fd", + "SandboxKey": "" + }, + "Path": "/usr/local/bin/mining-pool", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/af05c37ea4e014bead3c9856059c921c57b7ae523efdb9201a8ffbbee724f93d/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3631792, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T23:31:44.6094248Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "2026/06/22 04:15:26 \ud83d\udcb0 CREDIT SUMMARY block=e92c3dbe41598185d10c0ff32d3a73603ff89ce361f450f257b84fcd01000000 height=9364725 totalWork=999839221856 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364735 payoutUnlockHeight=9368821 suppressedDetailBlocks=0\n2026/06/22 04:15:29 [ROUTER] preserving current jobs for backend=node during same-parent template invalidation reason=node template invalidation: parent_changed\n2026/06/22 04:15:29 [REFRESH] node template invalidation: parent_changed (seq=25937 parent=8102f4c359afd0cb3806050b980572c6714e21ccb823fda70c720941c9e1a736)\n2026/06/22 04:15:29 [REFRESH] same-parent template refresh preserved current jobs reason=node template invalidation: parent_changed (seq=25937 parent=8102f4c359afd0cb3806050b980572c6714e21ccb823fda70c720941c9e1a736)\n2026/06/22 04:15:29 [REFRESH] clean template refresh invalidated_jobs=0 reason=node template invalidation: parent_changed\n2026/06/22 04:15:29 \ud83c\udfaf BLOCK FOUND height(le)=9364727 job=25937-18bb4c14c9d8b505_18000000 hash=65f92876cbdaab5d0f28777963df7695a14b4a8713834befb1ba6b1f01000000 target=02c1eb0000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:29 \ud83c\udfaf BLOCK FOUND height(le)=9364727 job=25937-18bb4c14c9d8b505_05000000 hash=72ba8edabeeabec34e4d36089f39f830028468e6ef5e9295db578cef00000000 target=02c1eb0000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:30 \u2705 Block submitted successfully: map[accepted:true block_hash:0xa66710d6572193f3d431d7cdaf198ce46fcf0dbe9cc3af490f6c1856bcc3b8b5 code:accepted coinbase_amount:10 coinbase_txid:0x6f1c7dce70fb73962621ffe5dfc69b121c7117403d9fe8c7281ff36ac9f299fc height:9.364727e+06 message:accepted miner_type:GBT_Worker order:12230682 template_seq:21610]\n2026/06/22 04:15:30 [REFRESH] accepted block parent advance (seq=25937 parent=a66710d6572193f3d431d7cdaf198ce46fcf0dbe9cc3af490f6c1856bcc3b8b5 suppressed=4)\n2026/06/22 04:15:30 \ud83d\udcb0 CREDIT SUMMARY block=65f92876cbdaab5d0f28777963df7695a14b4a8713834befb1ba6b1f01000000 height=9364727 totalWork=999829416736 accounts=1 reward=4538346226776563992 poolFeePct=1.00 fee=45383462267765640 distributable=4492962764508798352 matureHeight=9364737 payoutUnlockHeight=9368823 suppressedDetailBlocks=0\n2026/06/22 04:15:30 \u2705 Block submitted successfully: map[accepted:true block_hash:0x3c959ca619712b63436125ec49e983c2a6b433345fccbb77f22b9b4cc0d36822 code:accepted coinbase_amount:10 coinbase_txid:0x6f1c7dce70fb73962621ffe5dfc69b121c7117403d9fe8c7281ff36ac9f299fc height:9.364727e+06 message:accepted miner_type:GBT_Worker order:12230682 template_seq:21610]\n2026/06/22 04:15:30 [REFRESH] accepted block parent advance skipped: current parent already superseded (seq=25937 parent=a66710d6572193f3d431d7cdaf198ce46fcf0dbe9cc3af490f6c1856bcc3b8b5)\n2026/06/22 04:15:30 [REFRESH] ws tip change to 5e01e2441247e5f7631a61ede132e03a130aabc9b404f995dfa34aac82baf686 (seq=25938 parent=5e01e2441247e5f7631a61ede132e03a130aabc9b404f995dfa34aac82baf686 suppressed=2)\n2026/06/22 04:15:30 [REFRESH] block submission accepted (seq=25939 parent=5e01e2441247e5f7631a61ede132e03a130aabc9b404f995dfa34aac82baf686 suppressed=4)\n2026/06/22 04:15:30 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:30 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:30 \ud83d\udcb0 CREDIT SUMMARY block=72ba8edabeeabec34e4d36089f39f830028468e6ef5e9295db578cef00000000 height=9364727 totalWork=999829416736 accounts=1 reward=4538346226776563992 poolFeePct=1.00 fee=45383462267765640 distributable=4492962764508798352 matureHeight=9364737 payoutUnlockHeight=9368823 suppressedDetailBlocks=0\n2026/06/22 04:15:34 \u2705 Maturity: marked 5 blocks MATURE currentHeight=9364730 first=b963e43d2bbc6a04a3e1dcdecddf9047b6cbce4ffa365f3323e72f1501000000 last=59a1be1894cb88d002e0fac582b0a93eaed381cfb87762ac50b14a4401000000 minConfs=10 maxConfs=17\n2026/06/22 04:15:34 \ud83d\udcb8 Processing Payouts for Block 969b4cd458a2eab7e851de0929c7d033e7d73627fc0bbb2d72d0c82701000000 (1 credits)\n2026/06/22 04:15:34 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:34 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x14635f132e5cead5ec706dfe9a97a4541d5d52f41bd3be7a464995dc99c80d73)\n2026/06/22 04:15:34 \ud83d\udcb0 Block 969b4cd458a2eab7e851de0929c7d033e7d73627fc0bbb2d72d0c82701000000 marked as PAID\n2026/06/22 04:15:34 \ud83d\udcb8 Processing Payouts for Block bee5dad8b2142ae705c5c73958ae5597bd68f73fcddc038623c2fb1701000000 (1 credits)\n2026/06/22 04:15:34 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:34 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x92a6cdda3ff830c57ac28e8d1d058f5f95705b9d556bf0add950c19e9410628c)\n2026/06/22 04:15:34 \ud83d\udcb0 Block bee5dad8b2142ae705c5c73958ae5597bd68f73fcddc038623c2fb1701000000 marked as PAID\n2026/06/22 04:15:34 \ud83d\udcb8 Processing Payouts for Block cbcd78a88a29e667474f6e09835776c430e0d6bfc4d4499da820946001000000 (1 credits)\n2026/06/22 04:15:34 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:34 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xd57d32229d5aa694d73e6fe490a81b7a16bd601719b8530885c44a76a7253d52)\n2026/06/22 04:15:34 \ud83d\udcb0 Block cbcd78a88a29e667474f6e09835776c430e0d6bfc4d4499da820946001000000 marked as PAID\n2026/06/22 04:15:34 \ud83d\udcb8 Processing Payouts for Block a80c8ea581969bd1d34bf931c745f1c9ac2b7c4c8bd3b190d77e0b7f01000000 (1 credits)\n2026/06/22 04:15:34 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:34 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x2f58b4ef1c0d25e9d062113ed5fa0221dfde4d3f2ec7bfa24c69ff8b1a1b1286)\n2026/06/22 04:15:34 \ud83d\udcb0 Block a80c8ea581969bd1d34bf931c745f1c9ac2b7c4c8bd3b190d77e0b7f01000000 marked as PAID\n2026/06/22 04:15:34 \ud83d\udcb8 Processing Payouts for Block f1cb86e81c34c10a60a3f43093a1f127dcdf389f6c16dd3372193aaf00000000 (1 credits)\n2026/06/22 04:15:34 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:34 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x01e226348b0b8944ea363ad11fff55e6af42bf0966421c3a3ee90da9aab3304f)\n2026/06/22 04:15:34 \ud83d\udcb0 Block f1cb86e81c34c10a60a3f43093a1f127dcdf389f6c16dd3372193aaf00000000 marked as PAID\n2026/06/22 04:15:34 \ud83c\udfaf BLOCK FOUND height(le)=9364732 job=25943-18bb4c15f6d9b5f5_18000000 hash=970e05639f620e4046809e6c29a67fb7d53c6db18f34045400daee1802000000 target=02c1eb0000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:34 \u2705 Block submitted successfully: map[accepted:true block_hash:0xdb75f3a3db87ec079f0f546d118af5527f03f6192ada945bd4e2a4b3524f1367 code:accepted coinbase_amount:10 coinbase_txid: height:9.364732e+06 message:accepted miner_type:GBT_Worker order:12230688 template_seq:21615]\n2026/06/22 04:15:34 \ud83d\udcb0 CREDIT SUMMARY block=970e05639f620e4046809e6c29a67fb7d53c6db18f34045400daee1802000000 height=9364732 totalWork=999857990044 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364742 payoutUnlockHeight=9368828 suppressedDetailBlocks=0\n2026/06/22 04:15:34 [REFRESH] ws TTL refresh (seq=25944 parent=db75f3a3db87ec079f0f546d118af5527f03f6192ada945bd4e2a4b3524f1367 suppressed=2)\n2026/06/22 04:15:34 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:34 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:35 \ud83c\udfaf BLOCK FOUND height(le)=9364733 job=25945-18bb4c16019c70a1_05000000 hash=d89d2adca1b001f707749c6c23ef11849e9d92f01df499a05bde0c3600000000 target=02c1eb0000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:35 \u2705 Block submitted successfully: map[accepted:true block_hash:0xa2be1f3c6872d9998c207746f43ca265e60739085e4afdc25b00b64434558901 code:accepted coinbase_amount:10 coinbase_txid: height:9.364733e+06 message:accepted miner_type:GBT_Worker order:12230689 template_seq:21616]\n2026/06/22 04:15:35 \ud83d\udcb0 CREDIT SUMMARY block=d89d2adca1b001f707749c6c23ef11849e9d92f01df499a05bde0c3600000000 height=9364733 totalWork=999892805359 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364743 payoutUnlockHeight=9368829 suppressedDetailBlocks=0\n2026/06/22 04:15:35 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:35 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:35 \ud83c\udfaf BLOCK FOUND height(le)=9364734 job=25947-18bb4c1609d3b4aa_18000000 hash=d08d8a612e865e8b8341e958204d84ed8c69c50a9c969ffedfd6bf6300000000 target=02c1eb0000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:36 \u2705 Block submitted successfully: map[accepted:true block_hash:0x276f72cdb273a75a4a68146150229e8fc6ad10e8e7f963900e5bf0e378fe8982 code:accepted coinbase_amount:10 coinbase_txid: height:9.364734e+06 message:accepted miner_type:GBT_Worker order:12230690 template_seq:21617]\n2026/06/22 04:15:36 [REFRESH] accepted block parent advance (seq=25947 parent=276f72cdb273a75a4a68146150229e8fc6ad10e8e7f963900e5bf0e378fe8982 suppressed=2)\n2026/06/22 04:15:36 [REFRESH] ws tip change to 4b64730311da624352cbe62f445b2dbfaac53aaee99d386d599482bbbf6586e0 (seq=25948 parent=4b64730311da624352cbe62f445b2dbfaac53aaee99d386d599482bbbf6586e0 suppressed=4)\n2026/06/22 04:15:36 [RECOVERY] resending current job to mac:28:e2:97:4d:44:3a addr:192.168.1.105:51188 after 1 stale-template races (job=25947-18bb4c1609d3b4aa_05000000 reason=stale-parent)\n2026/06/22 04:15:36 [REFRESH] block submission accepted (seq=25949 parent=4b64730311da624352cbe62f445b2dbfaac53aaee99d386d599482bbbf6586e0 suppressed=2)\n2026/06/22 04:15:36 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:36 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:36 \ud83d\udcb0 CREDIT SUMMARY block=d08d8a612e865e8b8341e958204d84ed8c69c50a9c969ffedfd6bf6300000000 height=9364734 totalWork=999938488889 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364744 payoutUnlockHeight=9368830 suppressedDetailBlocks=0\n2026/06/22 04:15:36 \ud83c\udfaf BLOCK FOUND height(le)=9364735 job=25949-18bb4c16452cdd7f_1b000000 hash=f32f3bbe91b7e70cc7d52a6f76a92d4fad95ad167d2531444f9cda2802000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:37 \u2705 valid share accepted 17947.063059 \u2192 1176178725 worker=0x1719E0ee598c15957448D5E568948101DF78e7A0 job=25949-18bb4c16452cdd7f_18000000 suppressed=3 suppressedDiff=51534.781930 suppressedWork=3377383469\n2026/06/22 04:15:37 \ud83c\udfaf BLOCK FOUND height(le)=9364735 job=25949-18bb4c16452cdd7f_18000000 hash=b3160afda51d49652d0ecaf72e8bf8c67971ebde87c215ff7663418201000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:37 \u2705 Block submitted successfully: map[accepted:true block_hash:0xbcf0d91aac0a7a401b04f669649ecd42e16ab424bf5c35da79ffb14ba274df2f code:accepted coinbase_amount:10 coinbase_txid: height:9.364735e+06 message:accepted miner_type:GBT_Worker order:12230692 template_seq:21618]\n2026/06/22 04:15:37 \ud83d\udcb0 CREDIT SUMMARY block=f32f3bbe91b7e70cc7d52a6f76a92d4fad95ad167d2531444f9cda2802000000 height=9364735 totalWork=999808483535 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364745 payoutUnlockHeight=9368831 suppressedDetailBlocks=0\n2026/06/22 04:15:38 \u2705 Block submitted successfully: map[accepted:true block_hash:0xb6783464cad6a1a98dd26f0522abb26eb00fed6fe0640e5dbe314134c3c8bf97 code:accepted coinbase_amount:10 coinbase_txid: height:9.364735e+06 message:accepted miner_type:GBT_Worker order:uncertainty template_seq:21620]\n2026/06/22 04:15:38 [REFRESH] accepted block parent advance skipped: current parent already superseded (seq=25951 parent=08e4ce99f8526c3bda6a6a9b1ac8dd4dd8a3fc1af87763a51aff96d033967987)\n2026/06/22 04:15:38 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:38 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:38 \ud83d\udcb0 CREDIT SUMMARY block=b3160afda51d49652d0ecaf72e8bf8c67971ebde87c215ff7663418201000000 height=9364735 totalWork=999808483535 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364745 payoutUnlockHeight=9368831 suppressedDetailBlocks=0\n2026/06/22 04:15:38 \ud83c\udfaf BLOCK FOUND height(le)=9364736 job=25953-18bb4c16c0d695aa_1b000000 hash=d222ba50c335212e3d97848b1f147b231cf2fd0016166b62da7eb3e701000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:40 [STRATUM] accepted client addr=192.168.1.14:37309 host=192.168.1.14 lane=mac:28:e2:97:3e:39:63 mac=28:e2:97:3e:39:63 suppressed=17\n2026/06/22 04:15:40 [STRATUM] client closed addr=192.168.1.14:37309 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=17\n2026/06/22 04:15:40 \ud83c\udfaf BLOCK FOUND height(le)=9364736 job=25953-18bb4c16c0d695aa_17000000 hash=69a7e00627095bcfb224cfd1d0f41fd8ec56ed072bbb982fe3ec906301000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:40 \u2705 Block submitted successfully: map[accepted:true block_hash:0xde3ed3ab54b0ba9306ad148b0531349e0c4c834e8b3cfe489e86c664519f47bd code:accepted coinbase_amount:10 coinbase_txid:0x14635f132e5cead5ec706dfe9a97a4541d5d52f41bd3be7a464995dc99c80d73 height:9.364736e+06 message:accepted miner_type:GBT_Worker order:uncertainty template_seq:21621]\n2026/06/22 04:15:40 \ud83d\udcb0 CREDIT SUMMARY block=d222ba50c335212e3d97848b1f147b231cf2fd0016166b62da7eb3e701000000 height=9364736 totalWork=999792259932 accounts=1 reward=4538346268776857992 poolFeePct=1.00 fee=45383462687768584 distributable=4492962806089089408 matureHeight=9364746 payoutUnlockHeight=9368832 suppressedDetailBlocks=0\n2026/06/22 04:15:40 \u2705 Block submitted successfully: map[accepted:true block_hash:0x21b03f78c057a7c46df8622d53c5935dab6126de6ff65718a913c737ea123abf code:accepted coinbase_amount:10 coinbase_txid:0x14635f132e5cead5ec706dfe9a97a4541d5d52f41bd3be7a464995dc99c80d73 height:9.364736e+06 message:accepted miner_type:GBT_Worker order:uncertainty template_seq:21622]\n2026/06/22 04:15:40 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:40 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:40 \u2705 Block & Credits saved to Database suppressed=37\n2026/06/22 04:15:40 \ud83d\udcb0 CREDIT SUMMARY block=69a7e00627095bcfb224cfd1d0f41fd8ec56ed072bbb982fe3ec906301000000 height=9364736 totalWork=999792259932 accounts=1 reward=4538346268776857992 poolFeePct=1.00 fee=45383462687768584 distributable=4492962806089089408 matureHeight=9364746 payoutUnlockHeight=9368832 suppressedDetailBlocks=0\n2026/06/22 04:15:41 [REFRESH] ws tip change to 6ee37ba8746111d0952acc985114fa8c76bab2f3639786305c426c1b8affe495 (seq=25957 parent=6ee37ba8746111d0952acc985114fa8c76bab2f3639786305c426c1b8affe495 suppressed=5)\n2026/06/22 04:15:42 [STRATUM] accepted client addr=192.168.1.101:36219 host=192.168.1.101 lane=mac:28:e2:97:1e:c0:b5 mac=28:e2:97:1e:c0:b5 suppressed=19\n2026/06/22 04:15:42 [STRATUM] client closed addr=192.168.1.101:36219 reason=eof authorized=false subscribed=false extranonce_subscribed=false user= suppressed=19\n2026/06/22 04:15:43 \ud83c\udfaf BLOCK FOUND height(le)=9364740 job=25957-18bb4c1787991076_1b000000 hash=3cc15926c22a87811491764fc77773231e3829fda89b8bec8533b57200000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:43 \u2705 Block submitted successfully: map[accepted:true block_hash:0x69eb6e1f1c9982d87a709e6e9a87f25898970ce99be2e4c0acdbafaf990d8d51 code:accepted coinbase_amount:10 coinbase_txid: height:9.36474e+06 message:accepted miner_type:GBT_Worker order:12230702 template_seq:21625]\n2026/06/22 04:15:43 [REFRESH] block submission accepted (seq=25959 parent=69eb6e1f1c9982d87a709e6e9a87f25898970ce99be2e4c0acdbafaf990d8d51 suppressed=2)\n2026/06/22 04:15:43 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:43 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:43 \ud83d\udcb0 CREDIT SUMMARY block=3cc15926c22a87811491764fc77773231e3829fda89b8bec8533b57200000000 height=9364740 totalWork=999944523717 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364750 payoutUnlockHeight=9368836 suppressedDetailBlocks=0\n2026/06/22 04:15:44 \ud83c\udfaf BLOCK FOUND height(le)=9364741 job=25959-18bb4c18130ecf36_18000000 hash=bc8fddf9ea7bf9dc8d179ba531375517c31aa267db36c32ec30451bd01000000 target=0267c60000000000000000000000000000000000000000000000000000\n2026/06/22 04:15:44 \u2705 Block submitted successfully: map[accepted:true block_hash:0xf59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8 code:accepted coinbase_amount:10 coinbase_txid: height:9.364741e+06 message:accepted miner_type:GBT_Worker order:12230703 template_seq:21625]\n2026/06/22 04:15:44 [REFRESH] accepted block parent advance (seq=25959 parent=f59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8 suppressed=2)\n2026/06/22 04:15:44 \ud83d\udcb0 CREDIT SUMMARY block=bc8fddf9ea7bf9dc8d179ba531375517c31aa267db36c32ec30451bd01000000 height=9364741 totalWork=999956172098 accounts=1 reward=4538346121775828992 poolFeePct=1.00 fee=45383461217758288 distributable=4492962660558070704 matureHeight=9364751 payoutUnlockHeight=9368837 suppressedDetailBlocks=0\n2026/06/22 04:15:44 [REFRESH] ws TTL refresh (seq=25960 parent=f59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8 suppressed=1)\n2026/06/22 04:15:44 [REFRESH] invalidated 4 current miner jobs by epoch reason=block submission accepted\n2026/06/22 04:15:44 [REFRESH] clean template refresh invalidated_jobs=4 reason=block submission accepted\n2026/06/22 04:15:44 [ROUTER] preserving current jobs for backend=node during same-parent template invalidation reason=node template invalidation: parent_changed\n2026/06/22 04:15:44 [REFRESH] node template invalidation: parent_changed (seq=25963 parent=f59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8)\n2026/06/22 04:15:44 [REFRESH] same-parent template refresh preserved current jobs reason=node template invalidation: parent_changed (seq=25963 parent=f59f06a9ba4be1434c4b38b234ce80d8a3e2f1927eb8775d321d74dabe63f0c8)\n2026/06/22 04:15:44 [REFRESH] clean template refresh invalidated_jobs=0 reason=node template invalidation: parent_changed\n2026/06/22 04:15:44 \u2705 Maturity: marked 6 blocks MATURE currentHeight=9364741 first=66b96dddba3480888b9a1a9e33e7f2d6702902d3f15c99226eb086f400000000 last=970e05639f620e4046809e6c29a67fb7d53c6db18f34045400daee1802000000 minConfs=10 maxConfs=20\n2026/06/22 04:15:44 \ud83d\udcb8 Processing Payouts for Block 831fa85d20f873ef5380f27a5787d4daa2ece698d3df50fef2235f4701000000 (1 credits)\n2026/06/22 04:15:44 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:44 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x279d06b6737824311fa57a614fdfc4f9936a7e202f44ac043540816eb8a999f8)\n2026/06/22 04:15:44 \ud83d\udcb0 Block 831fa85d20f873ef5380f27a5787d4daa2ece698d3df50fef2235f4701000000 marked as PAID\n2026/06/22 04:15:44 \ud83d\udcb8 Processing Payouts for Block e3103f0371dd04a7149c34f44ba697d597f1a98e2aecc550ce26f73501000000 (1 credits)\n2026/06/22 04:15:44 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:44 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x41a53831b2d92c0d84e14baee25b8e2c324e53ea569f99a05d9df48898eb0fac)\n2026/06/22 04:15:44 \ud83d\udcb0 Block e3103f0371dd04a7149c34f44ba697d597f1a98e2aecc550ce26f73501000000 marked as PAID\n2026/06/22 04:15:44 \ud83d\udcb8 Processing Payouts for Block a952a6d670b1f5f4cd9c1eced9253f32d3cd0de985564a30bf0e7c0001000000 (1 credits)\n2026/06/22 04:15:44 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:44 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0x3965aacdccd12141ed0057f40cf50c884545045c8fc3638c10af138851fc36f4)\n2026/06/22 04:15:44 \ud83d\udcb0 Block a952a6d670b1f5f4cd9c1eced9253f32d3cd0de985564a30bf0e7c0001000000 marked as PAID\n2026/06/22 04:15:44 \ud83d\udcb8 Processing Payouts for Block 6ca670a63dadf6f4cbb8ceebac3f2649f87ef10de23fbf0475dc7c2201000000 (1 credits)\n2026/06/22 04:15:44 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:44 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xbc3b30eb592437d9c43b717347c53a65b0ddfe8ee4f5f6194d5cee78e9fcc96c)\n2026/06/22 04:15:44 \ud83d\udcb0 Block 6ca670a63dadf6f4cbb8ceebac3f2649f87ef10de23fbf0475dc7c2201000000 marked as PAID\n2026/06/22 04:15:44 \ud83d\udcb8 Processing Payouts for Block fcd64c7591a68f9e2d13cb1218205c30cddc1934486cedc2ba1c899f00000000 (1 credits)\n2026/06/22 04:15:44 [Payout Debug] Address=0x1719E0ee598c15957448D5E568948101DF78e7A0 Amount=4492962660558070784 Wei, GasPrice=1000007, Limit=21000, TxCost=21000147000, TotalReq=4492962681558217784\n2026/06/22 04:15:44 Sent 4492962660558070784 to 0x1719E0ee598c15957448D5E568948101DF78e7A0 (Tx: 0xb6d850897657930f56fdc97382ffca0a0139a15dc4b7cec842962d22825f8869)\n2026/06/22 04:15:44 \ud83d\udcb0 Block fcd64c7591a68f9e2d13cb1218205c30cddc1934486cedc2ba1c899f00000000 marked as PAID", + "ports": "" + }, + "status-sampler": { + "image_latest_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "image_safepoint_id": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "inspect": { + "AppArmorProfile": "docker-default", + "Args": [ + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": false, + "AttachStdout": true, + "Cmd": null, + "Domainname": "", + "Entrypoint": [ + "/bin/sh", + "-c", + "exec python3 ops/status_sampler.py --loop --no-logs" + ], + "Env": [ + "BDAG_STATUS_SAMPLER_FILE=/home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json", + "BDAG_MINING_IMPERATIVE_GUARD_UNITS=", + "BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS=30", + "BDAG_CHAIN_STATE_SELF_HEAL_COMMAND=ops/chain-state-self-heal.sh --from-systemd", + "NODE_RPC_PASS=", + "BDAG_CHAIN_STATE_SELF_HEAL_UNIT=", + "BDAG_NODE_RPC_URLS=node=http://host.docker.internal:38131", + "BDAG_POOL_DB_CONTAINER=postgres", + "BDAG_NODE_SERVICES=node", + "BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60", + "BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL=http://dashboard:8088/api/status", + "BDAG_STACK_SERVICES=postgres,node,pool", + "BDAG_POOL_CONTAINERS=pool", + "BDAG_PROJECT_ROOT=/home/jeremy/blockdag-source/stack-redis", + "BDAG_POOL_HOST_NETWORK_METRICS_HOST=host.docker.internal", + "TMPDIR=/tmp", + "BDAG_POOL_CONTAINER=pool", + "COMPOSE_PROJECT_NAME=stack", + "BDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10", + "NODE_RPC_USER=test", + "BDAG_RUNTIME_DIR=/home/jeremy/blockdag-source/stack-redis/ops/runtime", + "BDAG_POOL_ENV_FILE=/home/jeremy/blockdag-source/stack-redis/.env", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DOCKER_VERSION=27.5.1", + "DOCKER_BUILDX_VERSION=0.20.1", + "DOCKER_COMPOSE_VERSION=2.33.0", + "DOCKER_TLS_CERTDIR=/certs", + "PYTHONUNBUFFERED=1" + ], + "Hostname": "dc4efa9d3462", + "Image": "stack-status-sampler", + "Labels": { + "com.docker.compose.config-hash": "9d60a4b5400ef9e3431e282622311aaaff140c76375cfffe3ac6613c28f1e091", + "com.docker.compose.container-number": "1", + "com.docker.compose.depends_on": "", + "com.docker.compose.image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "com.docker.compose.oneoff": "False", + "com.docker.compose.project": "stack", + "com.docker.compose.project.config_files": "/home/jeremy/blockdag-source/stack-redis/docker-compose.yml,/home/jeremy/blockdag-source/stack-redis/docker-compose.override.yml", + "com.docker.compose.project.working_dir": "/home/jeremy/blockdag-source/stack-redis", + "com.docker.compose.replace": "status-sampler", + "com.docker.compose.service": "status-sampler", + "com.docker.compose.version": "2.40.3", + "org.opencontainers.image.title": "BlockDAG Stack Status Sampler" + }, + "OpenStdin": false, + "StdinOnce": false, + "Tty": false, + "User": "1000:1000", + "Volumes": null, + "WorkingDir": "/home/jeremy/blockdag-source/stack-redis" + }, + "Created": "2026-06-21T21:55:34.980730129Z", + "Driver": "overlayfs", + "ExecIDs": null, + "HostConfig": { + "AutoRemove": false, + "Binds": [ + "/var/run/docker.sock:/var/run/docker.sock:rw", + "/home/jeremy/blockdag-source/stack-redis:/home/jeremy/blockdag-source/stack-redis:rw" + ], + "BlkioDeviceReadBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceWriteIOps": null, + "BlkioWeight": 100, + "BlkioWeightDevice": null, + "CapAdd": null, + "CapDrop": null, + "Cgroup": "", + "CgroupParent": "", + "CgroupnsMode": "private", + "ConsoleSize": [ + 0, + 0 + ], + "ContainerIDFile": "", + "CpuCount": 0, + "CpuPercent": 0, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpuShares": 256, + "CpusetCpus": "", + "CpusetMems": "", + "DeviceCgroupRules": null, + "DeviceRequests": null, + "Devices": null, + "Dns": null, + "DnsOptions": null, + "DnsSearch": null, + "ExtraHosts": [ + "host.docker.internal:host-gateway" + ], + "GroupAdd": [ + "115" + ], + "IOMaximumBandwidth": 0, + "IOMaximumIOps": 0, + "IpcMode": "private", + "Isolation": "", + "Links": null, + "LogConfig": { + "Config": { + "max-file": "2", + "max-size": "10m" + }, + "Type": "local" + }, + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/interrupts", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/sched_debug", + "/proc/scsi", + "/proc/timer_list", + "/proc/timer_stats", + "/sys/devices/virtual/powercap", + "/sys/firmware", + "/sys/devices/system/cpu/cpu0/thermal_throttle", + "/sys/devices/system/cpu/cpu1/thermal_throttle", + "/sys/devices/system/cpu/cpu2/thermal_throttle", + "/sys/devices/system/cpu/cpu3/thermal_throttle", + "/sys/devices/system/cpu/cpu4/thermal_throttle", + "/sys/devices/system/cpu/cpu5/thermal_throttle", + "/sys/devices/system/cpu/cpu6/thermal_throttle", + "/sys/devices/system/cpu/cpu7/thermal_throttle", + "/sys/devices/system/cpu/cpu8/thermal_throttle", + "/sys/devices/system/cpu/cpu9/thermal_throttle", + "/sys/devices/system/cpu/cpu10/thermal_throttle", + "/sys/devices/system/cpu/cpu11/thermal_throttle" + ], + "Memory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": null, + "NanoCpus": 0, + "NetworkMode": "stack_default", + "OomKillDisable": null, + "OomScoreAdj": 300, + "PidMode": "", + "PidsLimit": null, + "PortBindings": {}, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ], + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "unless-stopped" + }, + "Runtime": "runc", + "SecurityOpt": null, + "ShmSize": 67108864, + "Tmpfs": { + "/tmp": "size=128m,mode=1777" + }, + "UTSMode": "", + "Ulimits": null, + "UsernsMode": "", + "VolumeDriver": "", + "VolumesFrom": null + }, + "HostnamePath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hostname", + "HostsPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/hosts", + "Id": "dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757", + "Image": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739", + "ImageManifestDescriptor": { + "digest": "sha256:14ad5cf6cca8e3a1be3b533311cc93dc62783a0f8190e9edca3b760a3245c6af", + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "size": 2572 + }, + "LogPath": "", + "MountLabel": "", + "Mounts": [ + { + "Destination": "/var/run/docker.sock", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/var/run/docker.sock", + "Type": "bind" + }, + { + "Destination": "/home/jeremy/blockdag-source/stack-redis", + "Mode": "rw", + "Propagation": "rprivate", + "RW": true, + "Source": "/home/jeremy/blockdag-source/stack-redis", + "Type": "bind" + } + ], + "Name": "/status-sampler", + "NetworkSettings": { + "Networks": { + "stack_default": { + "Aliases": [ + "status-sampler", + "status-sampler" + ], + "DNSNames": [ + "status-sampler", + "dc4efa9d3462" + ], + "DriverOpts": null, + "EndpointID": "bea9145ad37eec88c0035936c500122a15538bbae3d48da30f659295fb0c2fca", + "Gateway": "172.18.0.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "GwPriority": 0, + "IPAMConfig": null, + "IPAddress": "172.18.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "Links": null, + "MacAddress": "4a:58:5e:1c:0a:0e", + "NetworkID": "328370722be4a541bcc4812540ee34c9e3533cfa7306236751ebed0efcfc95d9" + } + }, + "Ports": {}, + "SandboxID": "ac7a75b21d14e791247df23bd6b200be7bb07adfe71b74c3c2a21329237a4909", + "SandboxKey": "" + }, + "Path": "/bin/sh", + "Platform": "linux", + "ProcessLabel": "", + "ResolvConfPath": "/var/lib/docker/containers/dc4efa9d3462a26c48861783971efe5f73db00df5c6b44da69dec68678ab8757/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 3470155, + "Restarting": false, + "Running": true, + "StartedAt": "2026-06-21T21:55:45.539854201Z", + "Status": "running" + }, + "Storage": { + "RootFS": { + "Snapshot": { + "Name": "overlayfs" + } + } + } + }, + "logs_tail": "", + "ports": "" + } + }, + "created_at_sast": "2026-06-22T06:15:45+0200", + "dashboard_healthz": { + "global_age_seconds": 5, + "global_chain_latest_block": "11861911", + "global_latest_block": "11861911", + "global_status": "ok", + "global_stream_lag_blocks": "0", + "redis": true, + "status": "ok" + }, + "dashboard_live_status": { + "api_error": "", + "asic_rows": [ + { + "blocks_1h": "425", + "blocks_24h": "2230", + "blocks_30m": "198", + "health": "ok", + "ip": "192.168.1.101", + "job_age": "128 ms", + "last_block": "Jun 22, 2026 06:15:40 SAST", + "mac": "28:e2:97:1e:c0:b5", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "12" + }, + { + "blocks_1h": "394", + "blocks_24h": "2124", + "blocks_30m": "181", + "health": "ok", + "ip": "192.168.1.102", + "job_age": "128 ms", + "last_block": "Jun 22, 2026 06:15:43 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "9" + }, + { + "blocks_1h": "376", + "blocks_24h": "2014", + "blocks_30m": "180", + "health": "ok", + "ip": "192.168.1.105", + "job_age": "128 ms", + "last_block": "Jun 22, 2026 06:15:35 SAST", + "mac": "28:e2:97:4d:44:3a", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "10" + }, + { + "blocks_1h": "391", + "blocks_24h": "1879", + "blocks_30m": "169", + "health": "ok", + "ip": "192.168.1.14", + "job_age": "128 ms", + "last_block": "Jun 22, 2026 06:15:37 SAST", + "mac": "28:e2:97:3e:39:63", + "protocol": "stratum-v1-basic", + "ready": "yes", + "rejects_30m": "13" + } + ], + "chart_json": "[{\"color\":\"#2bd4a3\",\"label\":\"28:e2:97:1e:c0:b5\",\"mac\":\"28:e2:97:1e:c0:b5\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":62,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-21T23:30:00Z\"},{\"accepted\":88,\"rejected\":1,\"rejected_local\":7,\"t\":\"2026-06-21T23:40:00Z\"},{\"accepted\":78,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-21T23:50:00Z\"},{\"accepted\":62,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T00:00:00Z\"},{\"accepted\":91,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T00:10:00Z\"},{\"accepted\":90,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T00:20:00Z\"},{\"accepted\":85,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T00:30:00Z\"},{\"accepted\":63,\"rejected\":3,\"rejected_local\":2,\"t\":\"2026-06-22T00:40:00Z\"},{\"accepted\":73,\"rejected\":2,\"rejected_local\":5,\"t\":\"2026-06-22T00:50:00Z\"},{\"accepted\":72,\"rejected\":0,\"rejected_local\":7,\"t\":\"2026-06-22T01:00:00Z\"},{\"accepted\":89,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T01:10:00Z\"},{\"accepted\":99,\"rejected\":2,\"rejected_local\":9,\"t\":\"2026-06-22T01:20:00Z\"},{\"accepted\":58,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T01:30:00Z\"},{\"accepted\":69,\"rejected\":2,\"rejected_local\":6,\"t\":\"2026-06-22T01:40:00Z\"},{\"accepted\":93,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T01:50:00Z\"},{\"accepted\":92,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T02:00:00Z\"},{\"accepted\":82,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T02:10:00Z\"},{\"accepted\":88,\"rejected\":0,\"rejected_local\":8,\"t\":\"2026-06-22T02:20:00Z\"},{\"accepted\":72,\"rejected\":1,\"rejected_local\":7,\"t\":\"2026-06-22T02:30:00Z\"},{\"accepted\":88,\"rejected\":2,\"rejected_local\":5,\"t\":\"2026-06-22T02:40:00Z\"},{\"accepted\":68,\"rejected\":2,\"rejected_local\":2,\"t\":\"2026-06-22T02:50:00Z\"},{\"accepted\":74,\"rejected\":4,\"rejected_local\":7,\"t\":\"2026-06-22T03:00:00Z\"},{\"accepted\":69,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T03:10:00Z\"},{\"accepted\":71,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-22T03:20:00Z\"},{\"accepted\":68,\"rejected\":10,\"rejected_local\":2,\"t\":\"2026-06-22T03:30:00Z\"},{\"accepted\":88,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T03:40:00Z\"},{\"accepted\":82,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T03:50:00Z\"},{\"accepted\":80,\"rejected\":0,\"rejected_local\":1,\"t\":\"2026-06-22T04:00:00Z\"},{\"accepted\":36,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T04:10:00Z\"}],\"ready\":true},{\"color\":\"#f5c542\",\"label\":\"28:e2:97:3e:39:63\",\"mac\":\"28:e2:97:3e:39:63\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":57,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-21T23:30:00Z\"},{\"accepted\":79,\"rejected\":0,\"rejected_local\":11,\"t\":\"2026-06-21T23:40:00Z\"},{\"accepted\":73,\"rejected\":0,\"rejected_local\":2,\"t\":\"2026-06-21T23:50:00Z\"},{\"accepted\":78,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T00:00:00Z\"},{\"accepted\":72,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T00:10:00Z\"},{\"accepted\":63,\"rejected\":1,\"rejected_local\":4,\"t\":\"2026-06-22T00:20:00Z\"},{\"accepted\":73,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T00:30:00Z\"},{\"accepted\":82,\"rejected\":2,\"rejected_local\":3,\"t\":\"2026-06-22T00:40:00Z\"},{\"accepted\":81,\"rejected\":4,\"rejected_local\":19,\"t\":\"2026-06-22T00:50:00Z\"},{\"accepted\":66,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T01:00:00Z\"},{\"accepted\":74,\"rejected\":0,\"rejected_local\":1,\"t\":\"2026-06-22T01:10:00Z\"},{\"accepted\":64,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-22T01:20:00Z\"},{\"accepted\":66,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-22T01:30:00Z\"},{\"accepted\":54,\"rejected\":4,\"rejected_local\":0,\"t\":\"2026-06-22T01:40:00Z\"},{\"accepted\":82,\"rejected\":0,\"rejected_local\":7,\"t\":\"2026-06-22T01:50:00Z\"},{\"accepted\":69,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-22T02:00:00Z\"},{\"accepted\":28,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T02:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-22T02:20:00Z\"},{\"accepted\":57,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T02:30:00Z\"},{\"accepted\":59,\"rejected\":3,\"rejected_local\":8,\"t\":\"2026-06-22T02:40:00Z\"},{\"accepted\":60,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T02:50:00Z\"},{\"accepted\":72,\"rejected\":3,\"rejected_local\":7,\"t\":\"2026-06-22T03:00:00Z\"},{\"accepted\":79,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-22T03:10:00Z\"},{\"accepted\":82,\"rejected\":0,\"rejected_local\":2,\"t\":\"2026-06-22T03:20:00Z\"},{\"accepted\":67,\"rejected\":7,\"rejected_local\":2,\"t\":\"2026-06-22T03:30:00Z\"},{\"accepted\":73,\"rejected\":3,\"rejected_local\":4,\"t\":\"2026-06-22T03:40:00Z\"},{\"accepted\":68,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T03:50:00Z\"},{\"accepted\":59,\"rejected\":0,\"rejected_local\":2,\"t\":\"2026-06-22T04:00:00Z\"},{\"accepted\":42,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-22T04:10:00Z\"}],\"ready\":true},{\"color\":\"#ff7a7a\",\"label\":\"28:e2:97:4d:44:3a\",\"mac\":\"28:e2:97:4d:44:3a\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":54,\"rejected\":0,\"rejected_local\":2,\"t\":\"2026-06-21T23:30:00Z\"},{\"accepted\":86,\"rejected\":0,\"rejected_local\":7,\"t\":\"2026-06-21T23:40:00Z\"},{\"accepted\":81,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-21T23:50:00Z\"},{\"accepted\":68,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T00:00:00Z\"},{\"accepted\":92,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T00:10:00Z\"},{\"accepted\":67,\"rejected\":2,\"rejected_local\":5,\"t\":\"2026-06-22T00:20:00Z\"},{\"accepted\":80,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T00:30:00Z\"},{\"accepted\":61,\"rejected\":3,\"rejected_local\":5,\"t\":\"2026-06-22T00:40:00Z\"},{\"accepted\":72,\"rejected\":3,\"rejected_local\":6,\"t\":\"2026-06-22T00:50:00Z\"},{\"accepted\":66,\"rejected\":1,\"rejected_local\":4,\"t\":\"2026-06-22T01:00:00Z\"},{\"accepted\":77,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T01:10:00Z\"},{\"accepted\":88,\"rejected\":1,\"rejected_local\":9,\"t\":\"2026-06-22T01:20:00Z\"},{\"accepted\":70,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T01:30:00Z\"},{\"accepted\":59,\"rejected\":2,\"rejected_local\":4,\"t\":\"2026-06-22T01:40:00Z\"},{\"accepted\":71,\"rejected\":1,\"rejected_local\":10,\"t\":\"2026-06-22T01:50:00Z\"},{\"accepted\":63,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T02:00:00Z\"},{\"accepted\":71,\"rejected\":1,\"rejected_local\":3,\"t\":\"2026-06-22T02:10:00Z\"},{\"accepted\":70,\"rejected\":3,\"rejected_local\":5,\"t\":\"2026-06-22T02:20:00Z\"},{\"accepted\":67,\"rejected\":1,\"rejected_local\":7,\"t\":\"2026-06-22T02:30:00Z\"},{\"accepted\":73,\"rejected\":3,\"rejected_local\":3,\"t\":\"2026-06-22T02:40:00Z\"},{\"accepted\":68,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T02:50:00Z\"},{\"accepted\":76,\"rejected\":3,\"rejected_local\":6,\"t\":\"2026-06-22T03:00:00Z\"},{\"accepted\":58,\"rejected\":1,\"rejected_local\":0,\"t\":\"2026-06-22T03:10:00Z\"},{\"accepted\":64,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T03:20:00Z\"},{\"accepted\":68,\"rejected\":7,\"rejected_local\":2,\"t\":\"2026-06-22T03:30:00Z\"},{\"accepted\":64,\"rejected\":1,\"rejected_local\":3,\"t\":\"2026-06-22T03:40:00Z\"},{\"accepted\":71,\"rejected\":0,\"rejected_local\":2,\"t\":\"2026-06-22T03:50:00Z\"},{\"accepted\":72,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-22T04:00:00Z\"},{\"accepted\":37,\"rejected\":1,\"rejected_local\":3,\"t\":\"2026-06-22T04:10:00Z\"}],\"ready\":true},{\"color\":\"#7ca7ff\",\"label\":\"2a:71:c7:f5:1f:1e\",\"mac\":\"2a:71:c7:f5:1f:1e\",\"points\":[{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T04:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T05:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T06:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T07:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T08:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T09:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T10:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T11:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T12:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T13:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T14:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T15:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T16:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T17:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T18:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T19:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T20:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T21:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:20:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:30:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:40:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T22:50:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:00:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:10:00Z\"},{\"accepted\":0,\"rejected\":0,\"rejected_local\":0,\"t\":\"2026-06-21T23:20:00Z\"},{\"accepted\":44,\"rejected\":0,\"rejected_local\":1,\"t\":\"2026-06-21T23:30:00Z\"},{\"accepted\":77,\"rejected\":3,\"rejected_local\":13,\"t\":\"2026-06-21T23:40:00Z\"},{\"accepted\":75,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-21T23:50:00Z\"},{\"accepted\":96,\"rejected\":1,\"rejected_local\":5,\"t\":\"2026-06-22T00:00:00Z\"},{\"accepted\":73,\"rejected\":2,\"rejected_local\":7,\"t\":\"2026-06-22T00:10:00Z\"},{\"accepted\":62,\"rejected\":0,\"rejected_local\":7,\"t\":\"2026-06-22T00:20:00Z\"},{\"accepted\":77,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T00:30:00Z\"},{\"accepted\":65,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T00:40:00Z\"},{\"accepted\":89,\"rejected\":2,\"rejected_local\":14,\"t\":\"2026-06-22T00:50:00Z\"},{\"accepted\":95,\"rejected\":0,\"rejected_local\":4,\"t\":\"2026-06-22T01:00:00Z\"},{\"accepted\":80,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T01:10:00Z\"},{\"accepted\":67,\"rejected\":1,\"rejected_local\":6,\"t\":\"2026-06-22T01:20:00Z\"},{\"accepted\":70,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T01:30:00Z\"},{\"accepted\":82,\"rejected\":2,\"rejected_local\":5,\"t\":\"2026-06-22T01:40:00Z\"},{\"accepted\":90,\"rejected\":0,\"rejected_local\":5,\"t\":\"2026-06-22T01:50:00Z\"},{\"accepted\":79,\"rejected\":0,\"rejected_local\":11,\"t\":\"2026-06-22T02:00:00Z\"},{\"accepted\":83,\"rejected\":1,\"rejected_local\":4,\"t\":\"2026-06-22T02:10:00Z\"},{\"accepted\":82,\"rejected\":0,\"rejected_local\":6,\"t\":\"2026-06-22T02:20:00Z\"},{\"accepted\":75,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T02:30:00Z\"},{\"accepted\":69,\"rejected\":2,\"rejected_local\":4,\"t\":\"2026-06-22T02:40:00Z\"},{\"accepted\":69,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T02:50:00Z\"},{\"accepted\":70,\"rejected\":2,\"rejected_local\":9,\"t\":\"2026-06-22T03:00:00Z\"},{\"accepted\":61,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T03:10:00Z\"},{\"accepted\":78,\"rejected\":0,\"rejected_local\":1,\"t\":\"2026-06-22T03:20:00Z\"},{\"accepted\":65,\"rejected\":4,\"rejected_local\":4,\"t\":\"2026-06-22T03:30:00Z\"},{\"accepted\":70,\"rejected\":2,\"rejected_local\":3,\"t\":\"2026-06-22T03:40:00Z\"},{\"accepted\":71,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T03:50:00Z\"},{\"accepted\":73,\"rejected\":0,\"rejected_local\":3,\"t\":\"2026-06-22T04:00:00Z\"},{\"accepted\":37,\"rejected\":1,\"rejected_local\":2,\"t\":\"2026-06-22T04:10:00Z\"}],\"ready\":true}]", + "evidence": [ + { + "label": "Live Mode", + "value": "mining" + }, + { + "label": "Live Overall", + "value": "ok" + }, + { + "label": "Miner Activity", + "value": "yes" + }, + { + "label": "Accepting Shares", + "value": "yes" + }, + { + "label": "Submit Ready", + "value": "yes" + }, + { + "label": "Getting Fresh Work", + "value": "yes" + }, + { + "label": "Won a block in last 600?", + "value": "yes" + } + ], + "groups": null, + "node": { + "evidence": [ + { + "label": "Node Block Height", + "value": "12,230,704" + }, + { + "label": "Peer Main Order", + "value": "12,230,699" + }, + { + "label": "Peer Order Gap", + "value": "0" + }, + { + "label": "Peers", + "value": "17" + }, + { + "label": "EVM Block Height", + "value": "11,861,916" + } + ], + "state": "Synced", + "status_steps": [ + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "P2P Down" + }, + { + "active": false, + "label": "Syncing" + }, + { + "active": true, + "label": "Synced" + }, + { + "active": false, + "label": "Node Degraded" + } + ], + "title": "Node Status", + "tone": "ok" + }, + "notes": null, + "refresh_every": "5s", + "rows": [ + { + "identity": "28:e2:97:1e:c0:b5", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.101", + "last": "Jun 22, 2026 06:15:43 SAST", + "mac": "28:e2:97:1e:c0:b5", + "observed": "192.168.1.101", + "shares": "0", + "status": "ok" + }, + { + "identity": "2a:71:c7:f5:1f:1e", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.102", + "last": "Jun 22, 2026 06:15:43 SAST", + "mac": "2a:71:c7:f5:1f:1e", + "observed": "192.168.1.102", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:4d:44:3a", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.105", + "last": "Jun 22, 2026 06:15:43 SAST", + "mac": "28:e2:97:4d:44:3a", + "observed": "192.168.1.105", + "shares": "0", + "status": "ok" + }, + { + "identity": "28:e2:97:3e:39:63", + "intent": "stratum+tcp://192.168.1.100:3334", + "ip": "192.168.1.14", + "last": "Jun 22, 2026 06:15:43 SAST", + "mac": "28:e2:97:3e:39:63", + "observed": "192.168.1.14", + "shares": "0", + "status": "ok" + } + ], + "secondary": [ + { + "label": "Miner Inventory", + "value": "4" + }, + { + "label": "Valid Shares", + "value": "8775" + }, + { + "label": "Last Share Age", + "value": "not known" + }, + { + "label": "Submit Errors", + "value": "124" + }, + { + "label": "Template Coinbase Valid", + "value": "unknown" + }, + { + "label": "Zero Coinbase Reject Rate", + "value": "unknown" + }, + { + "label": "Ready Miner Jobs", + "value": "4" + }, + { + "label": "Local Stale Ratio", + "value": "unknown" + }, + { + "label": "Last Automation Restart", + "value": "none" + }, + { + "label": "Pool Endpoint", + "value": "192.168.1.100:3334" + } + ], + "state": "Mining", + "state_tone": "ok", + "status_steps": [ + { + "active": false, + "label": "Waiting for Live Data" + }, + { + "active": false, + "label": "Down" + }, + { + "active": false, + "label": "Waiting for a Miner" + }, + { + "active": false, + "label": "Waiting for Node Sync" + }, + { + "active": false, + "label": "Template Unsafe" + }, + { + "active": false, + "label": "Degraded" + }, + { + "active": true, + "label": "Mining" + } + ], + "title": "Pool Status", + "updated_at": "Jun 22, 2026 06:15:45 SAST", + "warnings": null + }, + "dashboard_status": { + "asic_performance": { + "age_seconds": 2, + "bucket_seconds": 600, + "generated_at_unix_ms": 1782101743966, + "lanes": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "blocks_1h": 425, + "blocks_24h": 2230, + "blocks_30m": 198, + "blocks_6h": 2230, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 62, + "rejected": 0, + "rejected_local": 3, + "total": 65 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 88, + "rejected": 1, + "rejected_local": 7, + "total": 96 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 5, + "total": 83 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 62, + "rejected": 1, + "rejected_local": 2, + "total": 65 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 91, + "rejected": 0, + "rejected_local": 6, + "total": 97 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 90, + "rejected": 0, + "rejected_local": 3, + "total": 93 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 85, + "rejected": 0, + "rejected_local": 3, + "total": 88 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 63, + "rejected": 3, + "rejected_local": 2, + "total": 68 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 73, + "rejected": 2, + "rejected_local": 5, + "total": 80 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 7, + "total": 79 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 89, + "rejected": 0, + "rejected_local": 6, + "total": 95 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 99, + "rejected": 2, + "rejected_local": 9, + "total": 110 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 58, + "rejected": 0, + "rejected_local": 3, + "total": 61 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 69, + "rejected": 2, + "rejected_local": 6, + "total": 77 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 93, + "rejected": 0, + "rejected_local": 6, + "total": 99 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 92, + "rejected": 0, + "rejected_local": 3, + "total": 95 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 3, + "total": 85 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 88, + "rejected": 0, + "rejected_local": 8, + "total": 96 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 72, + "rejected": 1, + "rejected_local": 7, + "total": 80 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 88, + "rejected": 2, + "rejected_local": 5, + "total": 95 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 68, + "rejected": 2, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 74, + "rejected": 4, + "rejected_local": 7, + "total": 85 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 4, + "total": 75 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 68, + "rejected": 10, + "rejected_local": 2, + "total": 80 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 88, + "rejected": 0, + "rejected_local": 3, + "total": 91 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 5, + "total": 87 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 1, + "total": 81 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 36, + "rejected": 0, + "rejected_local": 6, + "total": 42 + }, + "start_unix_ms": 1782101400000 + } + ], + "client_addr": "192.168.1.101:55185", + "current_job_age_ms": 128, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782101740355, + "last_event_unix_ms": 1782101740355, + "pdiff": 0.25962705308906225, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 0, + "rejected_local_30m": 12, + "remote_host": "192.168.1.101", + "totals": { + "accepted": 2230, + "rejected": 31, + "rejected_local": 131, + "total": 2392 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "blocks_1h": 391, + "blocks_24h": 1879, + "blocks_30m": 169, + "blocks_6h": 1879, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 57, + "rejected": 0, + "rejected_local": 3, + "total": 60 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 79, + "rejected": 0, + "rejected_local": 11, + "total": 90 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 2, + "total": 75 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 5, + "total": 83 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 6, + "total": 78 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 63, + "rejected": 1, + "rejected_local": 4, + "total": 68 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 3, + "total": 76 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 82, + "rejected": 2, + "rejected_local": 3, + "total": 87 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 81, + "rejected": 4, + "rejected_local": 19, + "total": 104 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 66, + "rejected": 0, + "rejected_local": 5, + "total": 71 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 74, + "rejected": 0, + "rejected_local": 1, + "total": 75 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 64, + "rejected": 1, + "rejected_local": 5, + "total": 70 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 66, + "rejected": 0, + "rejected_local": 4, + "total": 70 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 54, + "rejected": 4, + "rejected_local": 0, + "total": 58 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 7, + "total": 89 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 5, + "total": 75 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 28, + "rejected": 1, + "rejected_local": 2, + "total": 31 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 57, + "rejected": 0, + "rejected_local": 3, + "total": 60 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 59, + "rejected": 3, + "rejected_local": 8, + "total": 70 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 60, + "rejected": 1, + "rejected_local": 2, + "total": 63 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 72, + "rejected": 3, + "rejected_local": 7, + "total": 82 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 79, + "rejected": 1, + "rejected_local": 5, + "total": 85 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 2, + "total": 84 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 67, + "rejected": 7, + "rejected_local": 2, + "total": 76 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 73, + "rejected": 3, + "rejected_local": 4, + "total": 80 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 68, + "rejected": 0, + "rejected_local": 5, + "total": 73 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 59, + "rejected": 0, + "rejected_local": 2, + "total": 61 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 42, + "rejected": 1, + "rejected_local": 5, + "total": 48 + }, + "start_unix_ms": 1782101400000 + } + ], + "client_addr": "192.168.1.14:56446", + "current_job_age_ms": 128, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:3e:39:63", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782101737286, + "last_event_unix_ms": 1782101737286, + "pdiff": 0.22197370599191724, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 1, + "rejected_local_30m": 12, + "remote_host": "192.168.1.14", + "totals": { + "accepted": 1879, + "rejected": 33, + "rejected_local": 130, + "total": 2042 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "blocks_1h": 376, + "blocks_24h": 2014, + "blocks_30m": 180, + "blocks_6h": 2014, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 54, + "rejected": 0, + "rejected_local": 2, + "total": 56 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 86, + "rejected": 0, + "rejected_local": 7, + "total": 93 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 81, + "rejected": 0, + "rejected_local": 3, + "total": 84 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 68, + "rejected": 1, + "rejected_local": 2, + "total": 71 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 92, + "rejected": 0, + "rejected_local": 6, + "total": 98 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 67, + "rejected": 2, + "rejected_local": 5, + "total": 74 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 5, + "total": 85 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 61, + "rejected": 3, + "rejected_local": 5, + "total": 69 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 72, + "rejected": 3, + "rejected_local": 6, + "total": 81 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 66, + "rejected": 1, + "rejected_local": 4, + "total": 71 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 77, + "rejected": 0, + "rejected_local": 5, + "total": 82 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 88, + "rejected": 1, + "rejected_local": 9, + "total": 98 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 3, + "total": 73 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 59, + "rejected": 2, + "rejected_local": 4, + "total": 65 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 71, + "rejected": 1, + "rejected_local": 10, + "total": 82 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 63, + "rejected": 0, + "rejected_local": 5, + "total": 68 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 1, + "rejected_local": 3, + "total": 75 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 3, + "rejected_local": 5, + "total": 78 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 67, + "rejected": 1, + "rejected_local": 7, + "total": 75 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 73, + "rejected": 3, + "rejected_local": 3, + "total": 79 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 68, + "rejected": 0, + "rejected_local": 3, + "total": 71 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 76, + "rejected": 3, + "rejected_local": 6, + "total": 85 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 58, + "rejected": 1, + "rejected_local": 0, + "total": 59 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 64, + "rejected": 0, + "rejected_local": 3, + "total": 67 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 68, + "rejected": 7, + "rejected_local": 2, + "total": 77 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 64, + "rejected": 1, + "rejected_local": 3, + "total": 68 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 2, + "total": 73 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 4, + "total": 76 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 37, + "rejected": 1, + "rejected_local": 3, + "total": 41 + }, + "start_unix_ms": 1782101400000 + } + ], + "client_addr": "192.168.1.105:51188", + "current_job_age_ms": 128, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:28:e2:97:4d:44:3a", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782101735052, + "last_event_unix_ms": 1782101735052, + "pdiff": 0.22764823340664364, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 1, + "rejected_local_30m": 9, + "remote_host": "192.168.1.105", + "totals": { + "accepted": 2014, + "rejected": 35, + "rejected_local": 125, + "total": 2174 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "blocks_1h": 394, + "blocks_24h": 2124, + "blocks_30m": 181, + "blocks_6h": 2124, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782015600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782016800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 44, + "rejected": 0, + "rejected_local": 1, + "total": 45 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 77, + "rejected": 3, + "rejected_local": 13, + "total": 93 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 75, + "rejected": 0, + "rejected_local": 6, + "total": 81 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 96, + "rejected": 1, + "rejected_local": 5, + "total": 102 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 73, + "rejected": 2, + "rejected_local": 7, + "total": 82 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 62, + "rejected": 0, + "rejected_local": 7, + "total": 69 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 77, + "rejected": 0, + "rejected_local": 3, + "total": 80 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 65, + "rejected": 1, + "rejected_local": 2, + "total": 68 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 89, + "rejected": 2, + "rejected_local": 14, + "total": 105 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 95, + "rejected": 0, + "rejected_local": 4, + "total": 99 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 3, + "total": 83 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 67, + "rejected": 1, + "rejected_local": 6, + "total": 74 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 6, + "total": 76 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 82, + "rejected": 2, + "rejected_local": 5, + "total": 89 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 90, + "rejected": 0, + "rejected_local": 5, + "total": 95 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 79, + "rejected": 0, + "rejected_local": 11, + "total": 90 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 83, + "rejected": 1, + "rejected_local": 4, + "total": 88 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 6, + "total": 88 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 75, + "rejected": 0, + "rejected_local": 3, + "total": 78 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 69, + "rejected": 2, + "rejected_local": 4, + "total": 75 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 70, + "rejected": 2, + "rejected_local": 9, + "total": 81 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 61, + "rejected": 0, + "rejected_local": 3, + "total": 64 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 1, + "total": 79 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 65, + "rejected": 4, + "rejected_local": 4, + "total": 73 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 70, + "rejected": 2, + "rejected_local": 3, + "total": 75 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 3, + "total": 74 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 3, + "total": 76 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 37, + "rejected": 1, + "rejected_local": 2, + "total": 40 + }, + "start_unix_ms": 1782101400000 + } + ], + "client_addr": "192.168.1.102:44959", + "current_job_age_ms": 128, + "extranonce_subscribed": false, + "health": "ok", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782101743725, + "last_event_unix_ms": 1782101743725, + "pdiff": 0.134196921616115, + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": 1, + "rejected_local_30m": 8, + "remote_host": "192.168.1.102", + "totals": { + "accepted": 2124, + "rejected": 25, + "rejected_local": 145, + "total": 2294 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + } + ], + "last_update_age_seconds": 2, + "source": "pool-health-asic-performance", + "updated_at": "2026-06-22T04:15:43Z", + "updated_at_epoch": 1782101743, + "window_seconds": 86400 + }, + "can_accept_shares": true, + "can_submit_blocks": true, + "chain_block_count": "12230704", + "containers": { + "node": { + "running": true + }, + "pool": { + "running": true + } + }, + "miner_health": { + "configured_count": 4, + "connected_count": 4, + "connected_count_effective": 4, + "identity_contract": "mac-only", + "managed_count": 4, + "managed_miners": [ + { + "blocks_1h": "425", + "blocks_24h": "2230", + "blocks_30m": "198", + "blocks_6h": "2230", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782101740355", + "last_event_at_epoch_ms": "1782101740355", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.25962705308906225", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "12", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "394", + "blocks_24h": "2124", + "blocks_30m": "181", + "blocks_6h": "2124", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782101743725", + "last_event_at_epoch_ms": "1782101743725", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.134196921616115", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "8", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "376", + "blocks_24h": "2014", + "blocks_30m": "180", + "blocks_6h": "2014", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782101735052", + "last_event_at_epoch_ms": "1782101735052", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.22764823340664364", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "9", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "391", + "blocks_24h": "1879", + "blocks_30m": "169", + "blocks_6h": "1879", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782101737286", + "last_event_at_epoch_ms": "1782101737286", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.22197370599191724", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "12", + "source": "env-overrides", + "status": "ok" + } + ], + "miners": [ + { + "blocks_1h": "425", + "blocks_24h": "2230", + "blocks_30m": "198", + "blocks_6h": "2230", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782101740355", + "last_event_at_epoch_ms": "1782101740355", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.25962705308906225", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "0", + "rejected_local_30m": "12", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "394", + "blocks_24h": "2124", + "blocks_30m": "181", + "blocks_6h": "2124", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782101743725", + "last_event_at_epoch_ms": "1782101743725", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.134196921616115", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "8", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "376", + "blocks_24h": "2014", + "blocks_30m": "180", + "blocks_6h": "2014", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782101735052", + "last_event_at_epoch_ms": "1782101735052", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.22764823340664364", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "9", + "source": "env-overrides", + "status": "ok" + }, + { + "blocks_1h": "391", + "blocks_24h": "1879", + "blocks_30m": "169", + "blocks_6h": "1879", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": true, + "current_job_age_ms": "128", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "ok", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782101737286", + "last_event_at_epoch_ms": "1782101737286", + "last_seen_at": "2026-06-22T04:15:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.22197370599191724", + "protocol_variant": "stratum-v1-basic", + "ready": true, + "rejected_30m": "1", + "rejected_local_30m": "12", + "source": "env-overrides", + "status": "ok" + } + ], + "pool_ready_overlay": true, + "source": "redis-asic-status", + "source_key": "", + "tracked_count": 4 + }, + "mode": "mining", + "overall": "ok", + "pool_health": { + "accepted_block_count": "8247", + "block_submit_error_count": "124", + "block_submit_success_count": "8247", + "source_job_health": { + "authorized_miners": "4", + "ready_miners": "4" + }, + "stale_jobs": false, + "valid_share_count": "8775" + }, + "pool_metrics": { + "accepted_block_recent": true, + "accepted_block_submissions": 8247, + "accepted_block_submissions_delta": 3, + "accepted_block_submissions_rate_per_second": 0.6, + "active_connections": 4, + "authorized_miners": 4, + "backend_main_order": 12230681, + "backend_mineable": 1, + "backend_p2p_mining_fresh": 1, + "backend_peer_lead_blocks": 0, + "backend_submit_ready": 1, + "blocks_found_total": 8371, + "expired_miners": 0, + "invalidated_miners": 0, + "last_accepted_block_submission_age_seconds": 0, + "last_accepted_block_submission_at_epoch": 1782101743, + "ready_miners": 4, + "rejected_block_submissions": 124, + "shares_accepted_total": 8775, + "shares_rejected_total": 2939, + "stale_miners": 0, + "template_age_seconds": 1.677 + }, + "source": "redis-live", + "status": "ok", + "sync_progress": { + "best_peer_mainorder": 12230699, + "chain_block_count": 12230704, + "chain_rpc_source": "getBlockCount", + "chain_syncing": false, + "current_block": 12230704, + "eth_syncing": { + "currentBlock": "0xb4ff9c", + "highestBlock": "0xb5493b", + "startingBlock": "0x0" + }, + "evm_block_count": 11861916, + "evm_chain_syncing": true, + "evm_rpc_url": "http://host.docker.internal:18545", + "evm_sync_advisory": "eth_syncing active while native P2P mining state is current", + "highest_block": 12230704, + "mining_advisory_sync": true, + "native_is_current": true, + "p2p_connections": 17, + "p2p_network_gap": 0, + "p2p_network_height": 12230699, + "peer_count": 17, + "peer_mainorder_gap": 0, + "remaining_blocks": 0, + "source": "native-rpc", + "status": "synced", + "sync_current_block": 11861916, + "sync_highest_block": 11880763 + }, + "updated_at": "2026-06-22T04:15:45Z" + }, + "docker_ps": "NAMES STATUS IMAGE\npool Up 5 hours stack-pool\ndashboard Up 5 hours (healthy) stack-dashboard\nnode Up 6 hours stack-node\nstatus-sampler Up 6 hours stack-status-sampler\npostgres Up 8 hours (healthy) postgres:15-bookworm", + "docker_stats": "NAME CPU % MEM USAGE / LIMIT BLOCK I/O NET I/O\nnode 36.88% 8.539GiB / 15.31GiB 1.92GB / 651GB 0B / 0B\npool 0.20% 30.11MiB / 15.31GiB 64MB / 0B 0B / 0B\ndashboard 15.27% 50.9MiB / 15.31GiB 243MB / 2.21GB 946MB / 190MB\nstatus-sampler 0.00% 33.73MiB / 15.31GiB 266MB / 217MB 411MB / 43.8MB", + "env_file": "COMPOSE_PROJECT_NAME=stack\nDOCKERFILE=dockerfile-dev\nDOCKER_PLATFORM=linux/amd64\n# Optional overrides for dockerfile-dev named build contexts.\"\n# BLOCKDAG_CORECHAIN_CONTEXT=../blockdag-corechain\n# POOL_SRC_CONTEXT=../pool\n# CPU_MINER_SRC_CONTEXT=../cpu-miner\nCOLLECTOR_SRC_CONTEXT=/home/jeremy/blockdag-source/collector\n# DASHBOARD_SRC_CONTEXT=../dashboard2\"\n# Paths are relative to tarball root (same directory as docker-compose.yml).\n# To import a snapshot during build, place latest.bdsnap in the payload root and\n# set SNAPSHOT_PATH=./latest.bdsnap. The default marker means \"build without a\n# baked-in snapshot.\"\nSNAPSHOT_PATH=docker/no-snapshot.marker\n# SNAPSHOT_PATH=docker/latest.bdsnap\"\n\n# Snapshot download link for this node. The installer writes this based on the\n# selected install mode. Naming convention on the snapshot host:\n# latest.bdsnap -> non-archive (pruned) snapshot\n# latest-archive.bdsnap -> archive (full history) snapshot\n# The node entrypoint downloads from this URL at first start when no local\n# snapshot or chain data exists.\nBDAG_SNAPSHOT_URL=\n# Set to 1 to run the node in archival mode (--archival; consensus keeps all\n# blocks instead of pruning). Archive nodes should bootstrap from the archive\n# snapshot so historical data is actually present.\nBDAG_NODE_ARCHIVAL=0\n# Optional compose services:\n# blank -> base stack only\n# miner -> include CPU miner service (requires DOCKERFILE=dockerfile-dev)\n# COMPOSE_PROFILES=miner\n\n# Dashboard repository. Release builds always use the main branch.\nDASHBOARD_REPO=https://github.com/BlockdagEngineering/redis-dash.git\nP2P_PORT=8155\nBDAG_RPC_PORT=38131\nEVM_HTTP_PORT=18545\nEVM_WS_PORT=18546\nWALLET_RPC_URL=http://127.0.0.1:18545\nNODE_METRICS_PORT=6060\nPOOL_BIND_PORT=3334\nPOOL_API_PORT=9090\nCOLLECTOR_HOST_PORT=9280\nDASHBOARD_HOST_PORT=8088\n# Dashboard UI display timezone.\nDASHBOARD_TIMEZONE=Africa/Johannesburg\n# NODE_DATA_DIR=./node-data\n\n# Required for any pool/miner deployment. Do not use the zero address.\n# POOL_COINBASE_ADDRESS may be set separately when it must differ from the\n# displayed miner/pool identity; otherwise set MINING_POOL_ADDRESS only.\nMINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\n# Storage placement for constrained mining appliances. Release installers keep\n# growing node chain data on capacity storage, then move frequent small writes\n# such as Postgres and dashboard/runtime state to internal storage when it has\n# enough free space.\nBDAG_STORAGE_PROFILE=auto\nBDAG_CHAIN_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_DATA_DIR=/home/jeremy/blockdag-source/stack/data\nBDAG_NODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nBDAG_POSTGRES_DATA_DIR=./data/postgres\nBDAG_RUNTIME_DIR=./ops/runtime\nBDAG_STORAGE_MIN_CHAIN_FREE_GIB=50\nBDAG_STORAGE_MIN_RUNTIME_FREE_GIB=4\n\n# Ephemeral scratch policy. Small temporary files belong on RAM-backed tmpfs so\n# they do not add avoidable writes to the USB chain device. Large snapshot and\n# chain staging must stay on capacity storage unless explicitly sized.\nBDAG_EPHEMERAL_TMPFS_ENABLED=1\nBDAG_EPHEMERAL_DIR=/run/bdag-pool\nBDAG_HOST_TMPDIR=/run/bdag-pool/tmp\nBDAG_CONTAINER_TMPFS_SIZE=128m\nBDAG_NODE_TMPFS_SIZE=512m\nBDAG_NODE_SHM_SIZE=512m\nBDAG_BUILD_TMPDIR=./.build-tmp\nBDAG_BUILD_CACHE_WARN_GIB=4\nBDAG_ROOT_FREE_WARN_GIB=6\nBDAG_ROOT_FREE_FAIL_GIB=2\n\n# Single P2P host port used by Docker publishing, firewall, and local peer\n# advertisement.\nBDAG_P2P_PROTOCOLS=tcp\n# Standard mining-host performance defaults. The release topology has one\n# production node with a direct pool-to-node RPC path.\nBDAG_NODE_CACHE_MB=6144\nBDAG_EVM_CACHE_MB=6144\nBDAG_NODE_CACHE_DATABASE_PERCENT=70\nBDAG_NODE_OBSOLETE_HEIGHT=20\nBDAG_NODE_MINING_NO_PENDING_TX=0\nBDAG_BLOCK_READ_AHEAD_KB=1024\nBDAG_BLOCK_NR_REQUESTS=256\nBDAG_NODE_CPU_SHARES=6144\nBDAG_POOL_CPU_SHARES=5120\nBDAG_POOL_DB_CPU_SHARES=4096\nBDAG_DASHBOARD_CPU_SHARES=128\nBDAG_NODE_MEMORY_LOW=768M\nBDAG_POOL_MEMORY_LOW=256M\nBDAG_POOL_DB_MEMORY_LOW=512M\nBDAG_DASHBOARD_MEMORY_LOW=64M\nBDAG_MINING_ACTIVE_NODE_NICE=-10\nBDAG_MINING_POOL_NICE=-8\nBDAG_OBSERVABILITY_NICE=15\nBDAG_TUNE_NET_QDISC=1\nBDAG_STACK_NET_IFACE=\nPOOL_RPC_BACKENDS=\n\n# Release install support services. Fresh appliance installs apply the\n# non-destructive host profile by default, then install P2P/IPFS support timers\n# and the mining-host tuning timer. Set the strict toggles to fail the install\n# instead of warning when host-level service installation cannot complete.\nBDAG_INSTALL_APPLIANCE_HOST_PROFILE=1\nBDAG_INSTALL_APPLIANCE_PROFILE_DISABLE_SERVICES=0\nBDAG_INSTALL_APPLIANCE_PROFILE_RELOAD_DOCKER=1\nBDAG_INSTALL_APPLIANCE_PROFILE_STRICT=0\nBDAG_INSTALL_STACK_SUPPORT_SERVICES=1\nBDAG_INSTALL_STACK_SUPPORT_SERVICES_STRICT=0\n\n# Enables localhost/private-network pool runtime admin endpoints. Keep the pool\n# metrics/admin port bound to localhost on public hosts.\nPOOL_RUNTIME_ADMIN_ENABLED=false\nMETRICS_ADDR=0.0.0.0:9090\nPOOL_GBT_MIN_INTERVAL_MS=1100\nPOOL_GBT_PRESSURE_INTERVAL_MS=500\nPOOL_GBT_PRESSURE_WINDOW_SECONDS=10\nPOOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS=15\nPOOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS=30\n# Host network discovery is only for choosing this node's advertised P2P address.\n# Peer candidates should be complete P2P multiaddrs.\nBDAG_NETWORK_TOPOLOGY=auto\nBDAG_DETECTED_NETWORK_TOPOLOGY=\nBDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\nBDAG_ASIC_LAN_CIDRS=192.168.1.0/24\n# Optional route to miners on a remote LAN reached through a routed gateway\n# (e.g. a ZeroTier peer). When set, the miner-route compose service keeps the\n# host route alive so the watchdog can reach the miner HTTP API. Leave empty\n# when miners are on the host LAN.\nBDAG_MINER_ROUTE_SUBNET=\nBDAG_MINER_ROUTE_GATEWAY=\nBDAG_MINER_ROUTE_DEV=\n# Docker bridge/container networks must not be treated as ASIC LANs. Override\"\n# only if a real ASIC LAN intentionally uses these ranges.\nBDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12\nBDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0\nBDAG_P2P_ADVERTISE_IP=\nBDAG_P2P_INTERFACE=\nBDAG_CHAIN_PEERSTORE_PEER_EXTRACTION_ENABLED=1\nBDAG_CHAIN_PEERSTORE_LOG_TAIL=8000\nEXTRA_PEER_ADDRESSES=\n# When enabled by installer/watchdog for attached ASICs, this must include\n# --miner, --miningaddr=, and --maxinbound=1 on constrained\n# USB/router hosts. Do not add --allowminingwhennearlysynced or\n# --allowsubmitwhennotsynced; template/readiness health must fail closed when\n# P2P freshness or sync safety is unavailable.\nBDAG_NODE_MINING_ARGS=\nBDAG_NODE_CHAIN_RPC_TIMEOUT=8.0\nBDAG_NODE_CHAIN_RPC_RETRIES=2\nBDAG_POOL_RPC_REFUSED_WARN_SECONDS=120\nBDAG_HOST_PRESSURE_IOWAIT_WARN_PERCENT=25\nBDAG_HOST_PRESSURE_IOWAIT_WARN_SAMPLES=3\nBDAG_HOST_PRESSURE_HISTORY_SAMPLES=6\nBDAG_SHARED_STATUS_CACHE_ENABLED=1\nBDAG_SHARED_STATUS_CACHE_SECONDS=3.0\nBDAG_STATUS_PAYLOAD_STALE_AFTER_SECONDS=120\nBDAG_HOST_PROFILE=auto\nBDAG_ADAPTIVE_CONCURRENCY_ENABLED=1\nBDAG_ADAPTIVE_IOWAIT_WARN_PERCENT=25\nBDAG_ADAPTIVE_IO_SOME_AVG10_WARN=20.0\nBDAG_ADAPTIVE_CPU_SOME_AVG10_WARN=80.0\nBDAG_ADAPTIVE_CHAIN_RPC_WARN_MS=1000\nBDAG_STATUS_SAMPLER_ENABLED=1\nBDAG_STATUS_SAMPLER_INTERVAL_SECONDS=10\nBDAG_STATUS_SAMPLER_MAX_AGE_SECONDS=120\nBDAG_COLLECTOR_DIRECT_STATUS_FALLBACK=0\nBDAG_COLLECTOR_STATUS_CACHE_SECONDS=10\nBDAG_COLLECTOR_SAMPLER_CACHE_SECONDS=120\nBDAG_COLLECTOR_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_CHAIN_STATE_SELF_HEAL_ENABLED=1\nBDAG_CHAIN_STATE_SELF_HEAL_UNIT=bdag-chain-state-self-heal.service\nBDAG_MINING_IMPERATIVE_CHAIN_STATE_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_ENABLED=1\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_SECONDS=900\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS=1000\nBDAG_CHAIN_STATE_STALLED_IMPORT_RESTORE_GAP_GROWTH_BLOCKS=60\nBDAG_CHAIN_STATE_REUSE_EXISTING_SNAPSHOT=1\n# Optional trusted restore source for unattended chain-state self-heal.\"\n# Use key-based SSH for remote rsync sources; do not store passwords here.\nBDAG_CHAIN_STATE_RESTORE_SOURCE=\nBDAG_CHAIN_STATE_RESTORE_SNAPSHOT=\nBDAG_CHAIN_STATE_RESTORE_SSH_COMMAND=\nBDAG_DASHBOARD_DIRECT_STATUS_FALLBACK=0\nBDAG_DASHBOARD_STATUS_CACHE_SECONDS=10\nBDAG_DASHBOARD_SAMPLER_CACHE_SECONDS=120\nBDAG_DASHBOARD_STATUS_SAMPLE_WAIT_SECONDS=60\nBDAG_WATCHDOG_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_EARNINGS_SAMPLER_INTERVAL_SECONDS=60\nBDAG_COLLECTOR_GLOBAL_SAMPLER_INTERVAL_SECONDS=60\nBDAG_OPS_UID=1000\nBDAG_OPS_GID=1000\nBDAG_DOCKER_SOCKET_GID=115\nBDAG_GLOBAL_CACHE_TTL_SECONDS=60\nBDAG_GLOBAL_RPC_WORKERS=4\nBDAG_GLOBAL_BLOCK_WINDOW=600\nBDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS=30\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOTS=1\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_HOURS=720\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WINDOW_BLOCKS=64\nBDAG_INSTALL_REBUILD_DASHBOARD_PLOT_WORKERS=12\nBDAG_DASHBOARD_HISTORY_REBUILD_PRESERVE_ASIC_HISTORY=1\n# Optional native mining RPC list for Global chain-production stats.\"\n# EVM RPC variables are wallet/balance sources only and must not drive Global mining attribution.\nBDAG_GLOBAL_CHAIN_RPC_URLS=\nBDAG_MINER_SCAN_WORKERS=64\nBDAG_MINER_HASHRATE_PROBE_WORKERS=8\nBDAG_BACKGROUND_MAINTENANCE_BACKOFF_ENABLED=1\nBDAG_BACKGROUND_MAINTENANCE_SYNC_BACKOFF_BLOCKS=0\nBDAG_BACKGROUND_MAINTENANCE_IOWAIT_WARN_PERCENT=25\nBDAG_BACKGROUND_MAINTENANCE_IO_SOME_AVG10_WARN=20.0\nBDAG_BACKGROUND_MAINTENANCE_CPU_SOME_AVG10_WARN=80.0\nBDAG_BACKGROUND_MAINTENANCE_CHAIN_RPC_WARN_MS=1000\nBDAG_BACKGROUND_MAINTENANCE_POOL_READY_TASKS=rawdatadir_publish,ipfs_content_sidecar,ipfs_segment_writer\nBDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=2\nBDAG_ENTRYPOINT_CHOWN_MODE=needed\nBDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\n# Current Compose/service-name topology. Source Compose intentionally does not\n# pin container_name; Docker Compose may append project and ordinal suffixes to\n# concrete container names.\nBDAG_POOL_CONTAINER=pool\nBDAG_POOL_CONTAINERS=pool\nBDAG_POOL_DB_CONTAINER=postgres\nBDAG_NODE_SERVICES=node\nBDAG_STACK_SERVICES=postgres,node,pool\nBDAG_NODE_RPC_URLS=node=http://127.0.0.1:38131\n\n# Optional pre-start P2P snapshot bootstrap. When the node datadir is empty,\n# the node image uses ordinary trusted/addpeer candidates for FastSnap V2 first,\n# imports the verified snapshot, then normal FastSync catches the remaining tail.\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=90s\nBDAG_FASTSNAP_ARTIFACT_V2=1\n# Prefer Fast Artifact Sync V2 directory artifacts when a peer offers them.\n# This avoids building/importing a temporary .bdsnap archive. The archive\n# snapshot RPC path is used only when the serving peer lacks directory support.\nBDAG_FASTSNAP_DIRECTORY_MODE=1\nBDAG_FASTSNAP_DIRECTORY_STAGING=\nBDAG_FASTSNAP_DIRECTORY_REPLACE_EXISTING=1\nBDAG_FASTSNAP_DIRECTORY_MOVE_STAGING=1\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_PARALLELISM=4\nBDAG_FASTSNAP_LEDGER=\n# Archive FastSnap seed serving is deprecated for release installs.\n# The raw datadir FastArtifact source sidecar below is the preferred sync-source\n# path. Keep this disabled unless an offline export workflow is being\n# investigated away from the live mining host.\nBDAG_FASTSNAP_SEED_TIMER_ENABLED=0\n# Raw datadir FastArtifact source sidecar. Installer auto-enables the timer only\n# when chain/stage/artifact paths are not USB/removable and the host has enough\n# CPU, RAM, and disk headroom. Publishes require an explicit operator-approved\n# finalization window.\nSYNC_SOURCE_NODE=0\nBDAG_RAWDATADIR_ARTIFACT_BASE=./data-restore/rawdatadir\nBDAG_RAWDATADIR_ARTIFACT_KEEP=3\nBDAG_RAWDATADIR_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_REQUIRE_EVM_REFERENCE_FRESH=1\nBDAG_RAWDATADIR_MAX_EVM_REFERENCE_LAG=1000\nBDAG_PUBLIC_RPC_URLS=bdagscan-rpc=https://rpc.bdagscan.com,blockdag-engineering-rpc=https://rpc.blockdag.engineering\nBDAG_RAWDATADIR_SIDECAR_SOURCE=\nBDAG_RAWDATADIR_SIDECAR_DIR=\nBDAG_RAWDATADIR_SIDECAR_CONTENT_MODE=auto\nBDAG_RAWDATADIR_SIDECAR_CONTENT_BASE=./data-restore/rawdatadir-sidecar-content\nBDAG_RAWDATADIR_SIDECAR_CONTENT_KEEP=2\nBDAG_RAWDATADIR_SIDECAR_CONTENT_CHUNK_SIZE=67108864\nBDAG_RAWDATADIR_SIDECAR_CONTENT_REQUIRE_SIGNED=1\nBDAG_RAWDATADIR_SIDECAR_CONTENT_ALLOW_HOT_PUBLISH=0\nBDAG_RAWDATADIR_OPEN_SIDECAR_ENABLED=1\nBDAG_RAWDATADIR_OPEN_SIDECAR_BASE=./data-restore/rawdatadir-sidecar-open/mainnet\nBDAG_RAWDATADIR_OPEN_SIDECAR_KEEP=12\nBDAG_RAWDATADIR_SIDECAR_RSYNC_BWLIMIT=4096\nBDAG_RAWDATADIR_SIDECAR_DELAY_UPDATES=0\nBDAG_RAWDATADIR_ACTIVE_SERVICE=node\nBDAG_RAWDATADIR_FINALIZE=0\nBDAG_RAWDATADIR_MIN_FREE_GIB=100\nBDAG_RAWDATADIR_FREE_SPACE_MULTIPLIER=2.5\nBDAG_RAWDATADIR_REQUIRE_STATE_ROOT=1\nBDAG_RAWDATADIR_SIDECAR_USE_SUDO=auto\nBDAG_RAWDATADIR_ARCHIVE_USE_SUDO=auto\nBDAG_RAWDATADIR_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_RAWDATADIR_TRUSTED_SIGNERS=\n# Optional Fast Artifact Sync V2 directory hot-stage serving. Set these only on\n# nodes that maintain a verified directory artifact mirror. The manifest path is\n# allowed to be a sidecar outside the directory to avoid copying control files\n# into a future install target.\nBDAG_FASTSYNC_ARTIFACT_DIRECTORY=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST=\n\n# Lazy IPFS content sidecar. The rawdatadir sidecar seals immutable signed\n# file/chunk generations into BDAG_RAWDATADIR_SIDECAR_CONTENT_BASE; IPFS pins\n# only publishable generations after mining and host resource gates pass. IPFS\n# is byte transport; signed manifests and consensus validation remain\n# authoritative.\nBDAG_IPFS_CONTENT_SIDECAR_MODE=auto\nBDAG_IPFS_CONTENT_ARTIFACT_DIR=./data-restore/rawdatadir-sidecar-content/current\nBDAG_IPFS_CONTENT_ARTIFACT_MANIFEST=./data-restore/rawdatadir-sidecar-content/current/manifest.json\nBDAG_IPFS_CONTENT_ALLOW_UNSIGNED_ARTIFACT=0\nBDAG_IPFS_CONTENT_PUBLISH_IPNS=0\nBDAG_IPFS_CONTENT_IPNS_KEY=\nBDAG_IPFS_CONTENT_REPUBLISH_IPNS_WHILE_WAITING=1\nBDAG_IPFS_CONTENT_IPNS_TTL=1m\nBDAG_IPFS_CONTENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_CONTENT_ADD_ARGS=\"--recursive --cid-version=1 --raw-leaves --pin=true --quieter\"\nBDAG_IPFS_CONTENT_DISCOVERY_FILE=./ops/ipfs-content-discovery.json\nBDAG_IPFS_CONTENT_LATEST_IPNS=/ipns/k51qzi5uqu5djjlh4vxtmzyswx0qk4s3wdlf3yrpkszp38gq5sl71zcgmmc3jk\nBDAG_IPFS_CONTENT_DEFAULT_INDEX_CID=bafkreia7jk2ljqi3raiohugp6nw3633njfp7jmnuvqh47po52et4kupu2a\nBDAG_IPFS_CONTENT_DEFAULT_ROOT_CID=\nBDAG_IPFS_CONTENT_STATUS_FILE=./ops/runtime/ipfs-content-sidecar-status.json\nBDAG_IPFS_CONTENT_LATEST_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\nBDAG_IPFS_SEGMENT_WRITER_MODE=auto\nBDAG_IPFS_SEGMENT_START_POLICY=live_tail\nBDAG_IPFS_SEGMENT_FINALITY_LAG_ORDERS=600\nBDAG_IPFS_SEGMENT_ORDERS_PER_SEGMENT=300\nBDAG_IPFS_SEGMENT_MAX_SEGMENTS_PER_RUN=1\nBDAG_IPFS_SEGMENT_MAX_RPC_PER_SECOND=25\nBDAG_IPFS_SEGMENT_RPC_TIMEOUT=8\nBDAG_IPFS_SEGMENT_BLOCK_RPC_RETRIES=2\nBDAG_IPFS_SEGMENT_PUBLISH_IPNS=0\nBDAG_IPFS_SEGMENT_IPNS_KEY=\nBDAG_IPFS_SEGMENT_IPNS_TTL=1m\nBDAG_IPFS_SEGMENT_IPNS_LIFETIME=8760h\nBDAG_IPFS_SEGMENT_STATUS_FILE=./ops/runtime/ipfs-content/segment-writer-status.json\nBDAG_IPFS_SEGMENT_INDEX_PATH=./ops/runtime/ipfs-content/latest-index.json\n\n# FastSync candidates for new/empty nodes and startup peers. Entries must be\n# complete P2P multiaddrs with peer IDs. Address class is not a sync mode or\n# priority signal; libp2p/FastArtifact probing should pick useful peers by\n# measured P2P latency, manifest/chunk response, and sustained transfer.\nBDAG_FASTSYNC_PEER_ORDERING=p2p-latency\nBDAG_FASTSYNC_APPEND_ADDPEERS=1\nBDAG_FASTARTIFACTSYNC_ENABLED=1\nBOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSYNC_RANGE_BLOCKS=1024\nBDAG_FASTSYNC_PREPROCESS_WORKERS=1\nBDAG_SYNC_COORDINATOR_ACCELERATE_FASTSYNC=1\nBDAG_SYNC_COORDINATOR_FAST_RESTART_COOLDOWN_SECONDS=900\nBDAG_SYNC_COORDINATOR_RESTART_ON_STALE_IMPORT=1\nBDAG_CATCHUP_PAUSE_ENABLED=1\nBDAG_CATCHUP_PAUSE_ON_SYNCING=1\nBDAG_CATCHUP_PAUSE_THRESHOLD_BLOCKS=300\nBDAG_CATCHUP_IO_PRESSURE_PAUSE_ENABLED=1\nBDAG_CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS=25\nBDAG_CATCHUP_IOWAIT_WARN_PERCENT=15\nBDAG_CATCHUP_IO_SOME_AVG10_WARN=20.0\nBDAG_CATCHUP_IO_FULL_AVG10_WARN=10.0\nBDAG_CATCHUP_NODE_RECREATE_ENABLED=1\nBDAG_CATCHUP_NODE_CACHE_MB=6144\nBDAG_CATCHUP_NODE_CACHE_MIN_MB=1024\nBDAG_CATCHUP_NODE_CACHE_MEMORY_PERCENT=40\nBDAG_FAST_CATCHUP_ARTIFACT_MODE=auto\nBDAG_FAST_CATCHUP_ARTIFACT_RETRY_SECONDS=300\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_BEHIND_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_MIN_GAIN_BLOCKS=1000\nBDAG_FAST_CATCHUP_ARTIFACT_TRUST_ON_FIRST_SIGNED=1\nBDAG_FAST_CATCHUP_ALLOW_UNSIGNED_ARTIFACTS=0\nBDAG_FAST_CATCHUP_ARTIFACT_TIMEOUT=21600s\nNODE_RPC_USER=test\nNODE_RPC_PASS=\nNODE_RPC_URLS=http://127.0.0.1:38131\n# Optional block-candidate submit override. Leave blank to use NODE_RPC_URLS.\"\n# Named entries are allowed: local=http://node:38131\nPOOL_SUBMIT_RPC_URLS=\n# Extra node flags.\nNODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\nBDAG_FASTSYNC_SNAPSHOT_PATH=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_ID=\nBDAG_FASTSYNC_ARTIFACT_SIGNING_KEY_HEX=\nBDAG_FASTSYNC_ARTIFACT_MANIFEST_TTL=24h\nBDAG_FASTSNAP_ENABLED=1\nBDAG_FASTSNAP_REQUIRED=0\nBDAG_FASTSNAP_PEERS_FILE=/etc/bdagStack/node.conf\nBDAG_FASTSNAP_AUTO_MAX_PEERS=3\nBDAG_FASTSNAP_LEGACY_FALLBACK=0\n# Production pool deployments are mainnet-only. Install/runtime scripts reject\n# any rawdatadir or FastSnap network value other than mainnet.\nBDAG_FASTSNAP_NETWORK=mainnet\nBDAG_RAWDATADIR_NETWORK=mainnet\nBDAG_FASTSNAP_PEERS=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\nBDAG_FASTSNAP_MIN_TIP=0\nBDAG_FASTSNAP_TIMEOUT=300s\nBDAG_FASTSNAP_PARALLELISM=8\nBDAG_FASTSNAP_RETRIES=5\nBDAG_FASTSNAP_RETRY_DELAY=10s\nBDAG_FASTSNAP_TRUSTED_SIGNERS=\nBDAG_FASTSNAP_ALLOW_UNSIGNED=0\nBDAG_FASTSNAP_KEEP_ARCHIVE=0\nPOSTGRES_USER=bdag_pool\nPOSTGRES_DB=bdagpool\nPOSTGRES_PASSWORD=\n# ---------------------------------------------------------------------------\n# Mining pool \u2014 passed into the `pool` container environment.\n# ---------------------------------------------------------------------------\nPOOL_BIND_ADDR=192.168.1.100:3334\nPPLNS_N_WORK=1000\nPOOL_FEE_PERCENTAGE=1.0\nPOOL_BLOCK_MATURITY=10\nPOOL_PAYOUT_MATURITY=4096\n# CPU miner (only used when COMPOSE_PROFILES includes \"miner\").\nMINER_POOL_URL=stratum+tcp://192.168.1.100:3334\nMINER_POOL_PASS=\nMINER_WORKERS=1\n\n\n\n# ASIC block-candidate timing pinned to the best observed 2026-06-21\n# 05:40-05:57 SAST production window.\nPOOL_RPC_ROUTER_NODE_HEALTH_ENABLED=true\nPOOL_TEMPLATE_TTL_REFRESH_MS=100\nPOOL_SUBMIT_STALE_BLOCK_CANDIDATES=false\nPOOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750\nPOOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS=1750\nPOOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS=100\nPOOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS=100\nPOOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=true\nPOOL_STALE_RACE_REJECT_WINDOW_SECONDS=10\nPOOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=1\nPOOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0\nPOOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=5\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120\nPOOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60\nPOOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=true\nPOOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=true\nSTACK_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nBLOCKDAG_CORECHAIN_CONTEXT=/home/jeremy/blockdag-source/blockdag-corechain\nPOOL_SRC_CONTEXT=/home/jeremy/blockdag-source/pool\nCPU_MINER_SRC_CONTEXT=/home/jeremy/blockdag-source/stack-redis\nDASHBOARD_SRC_CONTEXT=/home/jeremy/blockdag-source/redis-dash\nCOMPOSE_PROFILES=\nNODE_DATA_DIR=/home/jeremy/blockdag-source/stack/data/node\nSNAPSHOT_HOST_PATH=./docker/no-snapshot.marker\nBDAG_REQUIRE_SNAPSHOT=0\nBDAG_ENABLE_NODE_MINING=1\nBDAG_NODE_MODULES=Blockdag,miner\nBDAG_POOL_HOST=192.168.1.100\nBDAG_POOL_URL=stratum+tcp://192.168.1.100:3334\nBDAG_MINER_SCAN_TARGET=192.168.1.0/24\nBDAG_MINER_CONTROL_ENABLED=0\nDASHBOARD_BIND=127.0.0.1\nNODE_RPC_URL=http://127.0.0.1:38131\nPOOL_DB_HOST=127.0.0.1\nPOOL_DB_PORT=55432\nMINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\nPOOL_PRIVATE_KEY=\nPOOL_ASIC_MAC_ALLOWLIST=2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\nPOOL_ASIC_MAC_OVERRIDES=192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\nPOOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp\nPOOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED=false\nPOOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS=30\nPOOL_RPC_ROUTER_EVM_REFERENCE_URLS=https://rpc.blockdag.engineering,https://rpc.bdagscan.com\nBDAG_POOL_START_GATE_MIN_PEERS=2\nBDAG_NODE_DEBUG_LEVEL=warn\nBDAG_NODE_NO_FILE_LOGGING=1\n", + "hard_safety_rule": "No build/redeploy/restart without a savepoint and p2p guard. Live node must stay Blockdag+miner with explicit miningaddr; do not restart node for dashboard work.", + "node_command_env": "[\"/usr/local/bin/docker-entrypoint-nodeworker.sh\",\"/usr/local/bin/nodeworker\",\"--node-binary=/usr/local/bin/blockdag-node\",\"--node-args=--configfile /etc/bdagStack/node.conf\",\"--rpc-url=ws://127.0.0.1:18546\",\"--dag-rpc-url=http://127.0.0.1:38131\",\"--persist-root=/var/lib/bdagStack/nodeworker\",\"--health-min-peers=2\",\"--rollout-window=30m\"] null [\"BDAG_ENTRYPOINT_CHOWN_MODE=needed\",\"BDAG_NODE_MINING_NO_PENDING_TX=0\",\"TEMP=/tmp\",\"BDAG_EPHEMERAL_DIR=/run/bdag-ephemeral\",\"MINING_POOL_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"NODE_ARGS_APPEND=--modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"TMP=/tmp\",\"BDAG_NODE_MODULES=Blockdag,miner\",\"MINING_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODEWORKER_LIVENESS_TIMEOUT=5m\",\"BDAG_ASIC_LAN_INTERFACE=enxc8a362eb4bdc\",\"SYNC_SOURCE_NODE=0\",\"BDAG_NODE_ARCHIVAL=0\",\"BDAG_SNAPSHOT_URL=\",\"P2P_PORT=8155\",\"BDAG_STORAGE_PROFILE=auto\",\"BDAG_DETECTED_NETWORK_TOPOLOGY=\",\"BDAG_ASIC_LAN_CIDRS=192.168.1.0/24\",\"BDAG_ENABLE_NODE_MINING=1\",\"BDAG_NODE_NO_FILE_LOGGING=1\",\"BDAG_NETWORK_TOPOLOGY=auto\",\"BOOTSTRAP_PEER_ADDRESSES=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\",\"BDAG_P2P_ADVERTISE_IP=\",\"BDAG_P2P_INTERFACE=\",\"BDAG_NODE_MINING_ARGS=\",\"TMPDIR=/tmp\",\"BDAG_NODE_OBSOLETE_HEIGHT=20\",\"POOL_COINBASE_ADDRESS=0x1719E0ee598c15957448D5E568948101DF78e7A0\",\"BDAG_NODE_DEBUG_LEVEL=debug\",\"BDAG_NETWORK=\",\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"]", + "node_conf": "# BlockDAG node configuration for the Docker Compose stack.\n\n# P2P listen\nlisten=0.0.0.0\nport=8150\n\n# BDAG RPC listen\nrpclisten=0.0.0.0:38131\n\n# EVM RPC listen\nevm.http.addr=0.0.0.0\nevm.http.port=18545\nevm.ws.addr=0.0.0.0\nevm.ws.port=18546\n\n\n# Bootstrap peers (mainnet seed nodes). Add one addpeer=... line per complete\n# BDAG P2P multiaddr. Keep only public peers that are currently reachable.\n\n\n\n\naddpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo\naddpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\naddpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz\naddpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD\naddpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU\naddpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm\naddpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv\naddpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn\naddpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta\naddpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ\naddpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME\naddpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus\naddpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG\naddpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ\naddpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY\naddpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G\naddpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7\naddpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1\naddpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP\naddpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X\naddpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB\naddpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9\n\n# Peer limit\nmaxpeers=350\ncache=6144\ncache.database=70\n\n# RPC auth. Must match NODE_RPC_USER / NODE_RPC_PASS in .env.\nrpcuser=test\nrpcpass=\n\n# Storage paths\ndatadir=/var/lib/bdagStack/node\nlogdir=/var/log/bdagStack\n\n# Native metrics endpoint\nmetrics=true\nprofile=0.0.0.0:6060\n\n# Keep empty unless node mining is intentionally enabled.\nminingaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n\nmodules=Blockdag,miner\n\nevm.http.api=eth,net,web3,txpool,debug\nhttp.readtimeout=45s\nhttp.writetimeout=45s\nhttp.idletimeout=45s\nevmenv=\"--rpc.allow-unprotected-txs --metrics --metrics.addr 0.0.0.0 --metrics.port 6060 --cache 6144\"\nrpcmaxclients=64\nrpcmaxclientsperhost=16\nrpcmaxconcurrentreqs=500\nrpcrequesttimeout=60s\nhttp.ratelimit=1800\nhttp.rateburst=3200\n", + "p2p_guard": { + "clean_no_hits": true, + "code": 1, + "ok": false, + "out": "" + }, + "pool_metrics_tail": "pool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"duplicate-block-global\",le=\"1000\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"duplicate-block-global\",le=\"+Inf\"} 1\npool_submit_job_sequence_distance_sum{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"duplicate-block-global\"} 1\npool_submit_job_sequence_distance_count{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"duplicate-block-global\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"1\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"2\"} 1\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"3\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"5\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"10\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"25\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"50\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"100\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"250\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"1000\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\",le=\"+Inf\"} 2\npool_submit_job_sequence_distance_sum{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\"} 4\npool_submit_job_sequence_distance_count{classification=\"retained_same_parent_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"old-template-sequence\"} 2\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1\"} 15\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"2\"} 190\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"3\"} 214\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"5\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"10\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"25\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"50\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"100\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"250\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"1000\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\",le=\"+Inf\"} 215\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 441\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"accepted-share\",pool_id=\"0\",reason=\"ok\"} 215\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"1\"} 0\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"2\"} 27\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"3\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"5\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"10\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"25\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"50\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"100\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"250\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"1000\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\",le=\"+Inf\"} 28\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\"} 57\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"rejected\",pool_id=\"0\",reason=\"low_difficulty\"} 28\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1\"} 7\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"2\"} 80\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"3\"} 220\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"5\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"10\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"25\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"50\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"100\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"250\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"1000\"} 233\npool_submit_job_sequence_distance_bucket{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\",le=\"+Inf\"} 233\npool_submit_job_sequence_distance_sum{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 626\npool_submit_job_sequence_distance_count{classification=\"retained_stale_share_grace\",outcome=\"rejected-local\",pool_id=\"0\",reason=\"stale-job\"} 233\n# HELP pool_submit_read_to_handle_latency_seconds Time between reading a mining.submit request from the Stratum socket and starting submit handling.\n# TYPE pool_submit_read_to_handle_latency_seconds histogram\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0001\"} 19748\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.0005\"} 20326\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.001\"} 20336\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"1\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"2\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"5\"} 20341\npool_submit_read_to_handle_latency_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 20341\npool_submit_read_to_handle_latency_seconds_sum{pool_id=\"0\"} 1.1025838279999924\npool_submit_read_to_handle_latency_seconds_count{pool_id=\"0\"} 20341\n# HELP pool_submit_stall_recoveries_total Pool-native recoveries after repeated block candidates produce no successful node submissions.\n# TYPE pool_submit_stall_recoveries_total counter\npool_submit_stall_recoveries_total{action=\"clean-refresh\",pool_id=\"0\",reason=\"http-503\"} 2\npool_submit_stall_recoveries_total{action=\"clean-refresh\",pool_id=\"0\",reason=\"stale-job\"} 1\npool_submit_stall_recoveries_total{action=\"started\",pool_id=\"0\",reason=\"http-503\"} 2\npool_submit_stall_recoveries_total{action=\"started\",pool_id=\"0\",reason=\"stale-job\"} 1\n# HELP pool_submit_stall_window_failures Submit-stall recovery failures observed in the current window by kind.\n# TYPE pool_submit_stall_window_failures gauge\npool_submit_stall_window_failures{kind=\"all\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"duplicate\",pool_id=\"0\"} 0\npool_submit_stall_window_failures{kind=\"stale\",pool_id=\"0\"} 0\n# HELP pool_template_broadcasts_total Total block templates broadcast to connected miners.\n# TYPE pool_template_broadcasts_total counter\npool_template_broadcasts_total{pool_id=\"0\"} 25963\n# HELP pool_template_conversion_stall_active_miners Active authorized miners observed by the template-conversion stall detector.\n# TYPE pool_template_conversion_stall_active_miners gauge\npool_template_conversion_stall_active_miners{pool_id=\"0\"} 4\n# HELP pool_template_conversion_stall_failure_ratio Failed candidate block percentage in the current template-conversion stall window.\n# TYPE pool_template_conversion_stall_failure_ratio gauge\npool_template_conversion_stall_failure_ratio{pool_id=\"0\"} 3.8461538461538463\n# HELP pool_template_conversion_stall_window_candidates Candidate block outcomes observed in the current template-conversion stall window by kind.\n# TYPE pool_template_conversion_stall_window_candidates gauge\npool_template_conversion_stall_window_candidates{kind=\"accepted\",pool_id=\"0\"} 25\npool_template_conversion_stall_window_candidates{kind=\"duplicate\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"failed\",pool_id=\"0\"} 1\npool_template_conversion_stall_window_candidates{kind=\"stale\",pool_id=\"0\"} 0\npool_template_conversion_stall_window_candidates{kind=\"total\",pool_id=\"0\"} 26\n# HELP pool_template_fetch_duration_seconds Time to fetch a block template from the node via RPC.\n# TYPE pool_template_fetch_duration_seconds histogram\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.005\"} 11269\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.01\"} 13031\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.025\"} 19958\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.05\"} 22109\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.1\"} 22930\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.25\"} 23684\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"0.5\"} 24559\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"1\"} 25257\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"2\"} 25628\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"5\"} 25746\npool_template_fetch_duration_seconds_bucket{pool_id=\"0\",le=\"+Inf\"} 25746\npool_template_fetch_duration_seconds_sum{pool_id=\"0\"} 2044.4082803019992\npool_template_fetch_duration_seconds_count{pool_id=\"0\"} 25746\n# HELP pool_vardiff_adjustments_total VarDiff difficulty adjustments by direction.\n# TYPE pool_vardiff_adjustments_total counter\npool_vardiff_adjustments_total{direction=\"anti-spiral-hold\",pool_id=\"0\"} 29\npool_vardiff_adjustments_total{direction=\"decrease\",pool_id=\"0\"} 43\npool_vardiff_adjustments_total{direction=\"hold\",pool_id=\"0\"} 960\npool_vardiff_adjustments_total{direction=\"increase\",pool_id=\"0\"} 133\n# HELP pool_vardiff_floor_hits_total VarDiff retarget attempts clamped by the configured minimum pool difficulty.\n# TYPE pool_vardiff_floor_hits_total counter\npool_vardiff_floor_hits_total{pool_id=\"0\"} 10\n# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.\n# TYPE process_cpu_seconds_total counter\nprocess_cpu_seconds_total 236.54\n# HELP process_max_fds Maximum number of open file descriptors.\n# TYPE process_max_fds gauge\nprocess_max_fds 524287\n# HELP process_network_receive_bytes_total Number of bytes received by the process over the network.\n# TYPE process_network_receive_bytes_total counter\nprocess_network_receive_bytes_total 6.0361730779e+10\n# HELP process_network_transmit_bytes_total Number of bytes sent by the process over the network.\n# TYPE process_network_transmit_bytes_total counter\nprocess_network_transmit_bytes_total 4.5705130606e+10\n# HELP process_open_fds Number of open file descriptors.\n# TYPE process_open_fds gauge\nprocess_open_fds 18\n# HELP process_resident_memory_bytes Resident memory size in bytes.\n# TYPE process_resident_memory_bytes gauge\nprocess_resident_memory_bytes 3.264512e+07\n# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.\n# TYPE process_start_time_seconds gauge\nprocess_start_time_seconds 1.78208470456e+09\n# HELP process_virtual_memory_bytes Virtual memory size in bytes.\n# TYPE process_virtual_memory_bytes gauge\nprocess_virtual_memory_bytes 2.599038976e+09\n# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.\n# TYPE process_virtual_memory_max_bytes gauge\nprocess_virtual_memory_max_bytes 1.8446744073709552e+19\n# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.\n# TYPE promhttp_metric_handler_requests_in_flight gauge\npromhttp_metric_handler_requests_in_flight 1\n# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.\n# TYPE promhttp_metric_handler_requests_total counter\npromhttp_metric_handler_requests_total{code=\"200\"} 5108\npromhttp_metric_handler_requests_total{code=\"500\"} 0\npromhttp_metric_handler_requests_total{code=\"503\"} 0", + "previous_working_savepoint": "ops/savepoints/20260622-013454-post-sidebar-pool-working-retained.json", + "purpose": "Retained safe point before expanding redis-dash into integrated ASIC/pool/chain control and visualization suite.", + "rendered_compose_config": "name: stack\nservices:\n dashboard:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: dashboard\n cpu_shares: 256\n container_name: dashboard\n depends_on:\n node:\n condition: service_started\n required: true\n pool:\n condition: service_started\n required: true\n environment:\n ADDR: 0.0.0.0:8088\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_CHAIN_POLL_FALLBACK_AFTER_SECONDS: \"3\"\n BDAG_CHAIN_POLL_SECONDS: \"2\"\n BDAG_CHAIN_WS_READ_LIMIT_BYTES: \"1048576\"\n BDAG_CHAIN_WS_READ_TIMEOUT_SECONDS: \"10\"\n BDAG_DASHBOARD_HEALTH_STALE_SECONDS: \"20\"\n BDAG_DASHBOARD_REDIS_ENABLED: \"1\"\n BDAG_DASHBOARD_REDIS_MANAGED: \"1\"\n BDAG_EVM_HTTP_URL: http://host.docker.internal:18545\n BDAG_EVM_SYNC_BACKOFF_SECONDS: \"60\"\n BDAG_EVM_WS_URL: ws://host.docker.internal:18546\n BDAG_GLOBAL_BLOCK_WINDOW: \"600\"\n BDAG_GLOBAL_CACHE_MAX_TIP_LAG_BLOCKS: \"30\"\n BDAG_GLOBAL_FAST_WARMUP_BLOCKS: \"120\"\n BDAG_GLOBAL_MAX_INCREMENTAL_GAP: \"120\"\n BDAG_GLOBAL_RPC_CHUNK_SIZE: \"64\"\n BDAG_GLOBAL_RPC_WORKERS: \"4\"\n BDAG_GLOBAL_SETTLE_DELAY_SECONDS: \"5\"\n BDAG_MINER_CONTROL_ENABLED: \"0\"\n BDAG_NODE_RPC_URL: http://host.docker.internal:38131\n BDAG_POOL_METRICS_URL: http://host.docker.internal:9090/metrics\n BDAG_POOL_URL: stratum+tcp://192.168.1.100:3334\n BDAG_REDIS_APPENDFSYNC: everysec\n BDAG_REDIS_APPENDONLY: \"yes\"\n BDAG_REDIS_CONFIG: /etc/redis/redis.conf\n BDAG_REDIS_MAXMEMORY: 256mb\n BDAG_REDIS_MAXMEMORY_POLICY: noeviction\n BDAG_REDIS_SOCKET: /run/dashboard-redis/redis.sock\n BDAG_TREND_BACKFILL_CHUNK_SIZE: \"128\"\n BDAG_TREND_BACKFILL_DAYS: \"30\"\n BDAG_TREND_BACKFILL_MAX_BLOCKS_PER_PASS: \"4096\"\n BDAG_TREND_BACKFILL_SLEEP_MS: \"100\"\n BDAG_TREND_CATCHUP_INTERVAL_SECONDS: \"30\"\n BDAG_TREND_HISTORY_ENABLED: \"1\"\n BDAG_TREND_RECENT_WARMUP_BLOCKS: \"3600\"\n BDAG_TREND_RPC_WORKERS: \"4\"\n BDAG_TREND_SPARSE_MAX_SAMPLE_BLOCKS: \"2048\"\n BDAG_TREND_SPARSE_SAMPLES_PER_PASS: \"24\"\n DASHBOARD_TIMEZONE: Africa/Johannesburg\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n POOL_ASIC_ARP_TABLE_PATH: /host/proc/net/arp\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n healthcheck:\n test:\n - CMD-SHELL\n - curl -fsS http://127.0.0.1:8088/ >/dev/null\n timeout: 5s\n interval: 10s\n retries: 12\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 8088\n published: \"8088\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n - /run/dashboard-redis:size=16m,mode=0700\n volumes:\n - type: volume\n source: dashboard-redis\n target: /var/lib/dashboard-redis\n volume: {}\n - type: bind\n source: /proc/net/arp\n target: /host/proc/net/arp\n read_only: true\n bind:\n create_host_path: true\n miner-route:\n cap_add:\n - NET_ADMIN\n container_name: miner-route\n entrypoint:\n - /bin/sh\n - -c\n - 'while true; do if [ -n \"$$BDAG_MINER_ROUTE_SUBNET\" ] && [ -n \"$$BDAG_MINER_ROUTE_GATEWAY\" ]; then cmd=\"ip route replace $$BDAG_MINER_ROUTE_SUBNET via $$BDAG_MINER_ROUTE_GATEWAY\"; [ -n \"$$BDAG_MINER_ROUTE_DEV\" ] && cmd=\"$$cmd dev $$BDAG_MINER_ROUTE_DEV\"; $$cmd || echo \"miner-route: failed: $$cmd\"; fi; sleep 60; done'\n environment:\n BDAG_MINER_ROUTE_DEV: \"\"\n BDAG_MINER_ROUTE_GATEWAY: \"\"\n BDAG_MINER_ROUTE_SUBNET: \"\"\n image: alpine:3.20\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n restart: unless-stopped\n node:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: node\n cpu_shares: 4096\n container_name: node\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n BDAG_ASIC_LAN_INTERFACE: enxc8a362eb4bdc\n BDAG_DETECTED_NETWORK_TOPOLOGY: \"\"\n BDAG_ENABLE_NODE_MINING: \"1\"\n BDAG_ENTRYPOINT_CHOWN_MODE: needed\n BDAG_EPHEMERAL_DIR: /run/bdag-ephemeral\n BDAG_NETWORK: \"\"\n BDAG_NETWORK_TOPOLOGY: auto\n BDAG_NODE_ARCHIVAL: \"0\"\n BDAG_NODE_DEBUG_LEVEL: warn\n BDAG_NODE_MINING_ARGS: \"\"\n BDAG_NODE_MINING_NO_PENDING_TX: \"0\"\n BDAG_NODE_MODULES: Blockdag,miner\n BDAG_NODE_NO_FILE_LOGGING: \"1\"\n BDAG_NODE_OBSOLETE_HEIGHT: \"20\"\n BDAG_NODEWORKER_LIVENESS_TIMEOUT: 5m\n BDAG_P2P_ADVERTISE_IP: \"\"\n BDAG_P2P_INTERFACE: \"\"\n BDAG_SNAPSHOT_URL: \"\"\n BDAG_STORAGE_PROFILE: auto\n BOOTSTRAP_PEER_ADDRESSES: /ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz,/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU,/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus,/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X,/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops,/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1,/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo,/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7,/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP,/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP\n MINING_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_ARGS_APPEND: --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0\n P2P_PORT: \"8155\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n SYNC_SOURCE_NODE: \"0\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -900\n platform: linux/amd64\n restart: unless-stopped\n shm_size: \"536870912\"\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\n volumes:\n - type: bind\n source: /home/jeremy/blockdag-source/stack/data/node\n target: /var/lib/bdagStack/node\n bind:\n create_host_path: true\n - type: volume\n source: nodeworker-data\n target: /var/lib/bdagStack/nodeworker\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/docker/no-snapshot.marker\n target: /snapshot/latest.bdsnap\n read_only: true\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/node.conf\n target: /etc/bdagStack/node.conf\n read_only: true\n bind:\n create_host_path: true\n pool:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\n additional_contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\n target: pool\n cpu_shares: 3072\n container_name: pool\n depends_on:\n pool-db:\n condition: service_healthy\n required: true\n environment:\n BDAG_ASIC_LAN_CIDRS: 192.168.1.0/24\n METRICS_ADDR: 0.0.0.0:9090\n MINING_POOL_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n NODE_RPC_PASS: test\n NODE_RPC_URL: http://127.0.0.1:38131\n NODE_RPC_URLS: http://127.0.0.1:38131\n NODE_RPC_USER: test\n PG_URL: postgres://bdag_pool:06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803@127.0.0.1:55432/bdagpool?sslmode=disable\n POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB: \"true\"\n POOL_ASIC_MAC_ALLOWLIST: 2a:71:c7:f5:1f:1e,28:e2:97:3e:39:63,28:e2:97:1e:c0:b5,28:e2:97:4d:44:3a\n POOL_ASIC_MAC_OVERRIDES: 192.168.1.101=28:e2:97:1e:c0:b5,192.168.1.102=2a:71:c7:f5:1f:1e,192.168.1.14=28:e2:97:3e:39:63,192.168.1.105=28:e2:97:4d:44:3a\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE: \"true\"\n POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS: \"8000\"\n POOL_BIND_ADDR: 192.168.1.100:3334\n POOL_BLOCK_MATURITY: \"10\"\n POOL_BLOCK_TIMING_CONTROLLER_COOLDOWN_SECONDS: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_ENABLED: \"true\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_CANDIDATES: \"20\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS: \"1750\"\n POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS: \"100\"\n POOL_BLOCK_TIMING_CONTROLLER_STEP_MS: \"250\"\n POOL_BLOCK_TIMING_CONTROLLER_TARGET_WASTE_PERCENT: \"5\"\n POOL_BLOCK_TIMING_CONTROLLER_WINDOW_SECONDS: \"60\"\n POOL_COINBASE_ADDRESS: 0x1719E0ee598c15957448D5E568948101DF78e7A0\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: \"3\"\n POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_FEE_PERCENTAGE: \"1.0\"\n POOL_INSTANCE_ID: \"\"\n POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS: \"1750\"\n POOL_PAYOUT_MATURITY: \"4096\"\n POOL_PAYOUT_MAX_BLOCKS_PER_TICK: \"5\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED: \"false\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS: \"0\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED: \"true\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS: \"20\"\n POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS: \"1000\"\n POOL_PRIVATE_KEY: 11f16642b7153d33ef1f2029fad20e77489e38c801afe93ac5d1264238e6d780\n POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: \"1750\"\n POOL_RPC_BACKENDS: \"\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: \"false\"\n POOL_RPC_ROUTER_EVM_HEAD_GUARD_MAX_LAG_BLOCKS: \"30\"\n POOL_RPC_ROUTER_EVM_REFERENCE_URLS: https://rpc.blockdag.engineering,https://rpc.bdagscan.com\n POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: \"true\"\n POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS: \"15\"\n POOL_RPC_ROUTER_NODE_HEALTH_MAX_AGE_SECONDS: \"30\"\n POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS: \"2\"\n POOL_RPC_ROUTER_NODE_HEALTH_PROBE_SECONDS: \"15\"\n POOL_RUNTIME_ADMIN_ENABLED: \"false\"\n POOL_STALE_RACE_CLIENT_RECONNECT_COOLDOWN_SECONDS: \"60\"\n POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: \"0\"\n POOL_STALE_RACE_CLIENT_RECONNECT_WINDOW_SECONDS: \"120\"\n POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: \"1\"\n POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS: \"5\"\n POOL_STALE_RACE_REJECT_WINDOW_SECONDS: \"10\"\n POOL_STRATUM_CONNECTION_LOG_INTERVAL_SECONDS: \"60\"\n POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: \"true\"\n POOL_SUBMIT_RPC_URLS: \"\"\n POOL_SUBMIT_STALE_BLOCK_CANDIDATES: \"false\"\n POOL_TEMPLATE_TTL_REFRESH_MS: \"100\"\n PPLNS_N_WORK: \"1000\"\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n WALLET_RPC_URL: http://127.0.0.1:18545\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n network_mode: host\n oom_score_adj: -800\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n pool-db:\n cpu_shares: 3072\n container_name: postgres\n environment:\n POSTGRES_DB: bdagpool\n POSTGRES_PASSWORD: 06f09749c9e139cfde8c3f95c414b8de2bd6b3c13139c803\n POSTGRES_USER: bdag_pool\n TEMP: /tmp\n TMP: /tmp\n TMPDIR: /tmp\n healthcheck:\n test:\n - CMD-SHELL\n - pg_isready -U bdag_pool -d bdagpool\n timeout: 5s\n interval: 5s\n retries: 20\n image: postgres:15-bookworm\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: -800\n ports:\n - mode: ingress\n host_ip: 127.0.0.1\n target: 5432\n published: \"55432\"\n protocol: tcp\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n - /var/tmp:size=128m,mode=1777\n volumes:\n - type: volume\n source: postgres-data\n target: /var/lib/postgresql/data\n volume: {}\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis/sql/pool-schema.sql\n target: /docker-entrypoint-initdb.d/01-pool-schema.sql\n read_only: true\n bind:\n create_host_path: true\n sentinel:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: sentinel\n cpu_shares: 256\n container_name: sentinel\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner sentinel-container --owner-unit docker-compose:sentinel --reason 'provision automation control gate for containerised repairs' && while true; do python3 ops/stack_sentinel.py; sleep $${BDAG_SENTINEL_INTERVAL_SECONDS:-60}; done\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_SENTINEL_INTERVAL_SECONDS: \"60\"\n BDAG_SENTINEL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_SENTINEL_USER_SERVICES: \"\"\n BDAG_SENTINEL_USER_TIMERS: \"\"\n BDAG_STACK_SERVICES: postgres,node,pool\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n status-sampler:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: status-sampler\n blkio_config:\n weight: 100\n cpu_shares: 256\n container_name: status-sampler\n entrypoint:\n - /bin/sh\n - -c\n - exec python3 ops/status_sampler.py --loop --no-logs\n environment:\n BDAG_CHAIN_STATE_SELF_HEAL_COMMAND: ops/chain-state-self-heal.sh --from-systemd\n BDAG_CHAIN_STATE_SELF_HEAL_STATUS_URL: http://dashboard:8088/api/status\n BDAG_CHAIN_STATE_SELF_HEAL_UNIT: \"\"\n BDAG_MINING_IMPERATIVE_GUARD_UNITS: \"\"\n BDAG_MINING_IMPERATIVE_REPAIR_INTERVAL_SECONDS: \"30\"\n BDAG_NODE_RPC_URLS: node=http://host.docker.internal:38131\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SAMPLER_EARNINGS_SNAPSHOT_INTERVAL_SECONDS: \"60\"\n BDAG_STATUS_SAMPLER_FILE: /home/jeremy/blockdag-source/stack-redis/ops/runtime/status-sampler.json\n BDAG_STATUS_SAMPLER_INTERVAL_SECONDS: \"10\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\n watchdog:\n build:\n context: /home/jeremy/blockdag-source/stack-redis\n dockerfile: dockerfile-dev\n target: watchdog\n cpu_shares: 256\n container_name: watchdog\n entrypoint:\n - /bin/sh\n - -c\n - python3 ops/automation_control.py ensure-normal --owner watchdog-container --owner-unit docker-compose:watchdog --reason 'provision automation control gate for containerised repairs' && exec python3 ops/watchdog.py --loop\n environment:\n BDAG_NODE_SERVICES: node\n BDAG_POOL_CONTAINER: pool\n BDAG_POOL_CONTAINERS: pool\n BDAG_POOL_DB_CONTAINER: postgres\n BDAG_POOL_ENV_FILE: /home/jeremy/blockdag-source/stack-redis/.env\n BDAG_POOL_HOST: 192.168.1.100\n BDAG_POOL_HOST_NETWORK_METRICS_HOST: host.docker.internal\n BDAG_PROJECT_ROOT: /home/jeremy/blockdag-source/stack-redis\n BDAG_RUNTIME_DIR: /home/jeremy/blockdag-source/stack-redis/ops/runtime\n BDAG_STACK_SERVICES: postgres,node,pool\n BDAG_STATUS_SOURCE_URL: http://dashboard:8088/api/status\n BDAG_WATCHDOG_INTERVAL: \"60\"\n BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS: \"300\"\n COMPOSE_PROJECT_NAME: stack\n NODE_RPC_PASS: test\n NODE_RPC_USER: test\n TMPDIR: /tmp\n extra_hosts:\n - host.docker.internal=host-gateway\n group_add:\n - \"115\"\n logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\n networks:\n default: null\n oom_score_adj: 300\n platform: linux/amd64\n restart: unless-stopped\n tmpfs:\n - /tmp:size=128m,mode=1777\n user: 1000:1000\n volumes:\n - type: bind\n source: /var/run/docker.sock\n target: /var/run/docker.sock\n bind:\n create_host_path: true\n - type: bind\n source: /home/jeremy/blockdag-source/stack-redis\n target: /home/jeremy/blockdag-source/stack-redis\n bind:\n create_host_path: true\n working_dir: /home/jeremy/blockdag-source/stack-redis\nnetworks:\n default:\n name: stack_default\nvolumes:\n dashboard-redis:\n name: stack_dashboard-redis\n nodeworker-data:\n name: stack_nodeworker-data\n postgres-data:\n name: stack_postgres-data\nx-build-args:\n SNAPSHOT_PATH: docker/no-snapshot.marker\nx-dev-source-contexts:\n blockdag_corechain: /home/jeremy/blockdag-source/blockdag-corechain\n cpu_miner_src: /home/jeremy/blockdag-source/stack-redis\n dashboard_src: /home/jeremy/blockdag-source/redis-dash\n pool_src: /home/jeremy/blockdag-source/pool\n stack_src: /home/jeremy/blockdag-source/stack-redis\nx-mining-logging:\n driver: local\n options:\n max-file: \"2\"\n max-size: 10m\nx-node-critical:\n blkio_config:\n weight: 1000\n cpu_shares: 4096\n oom_score_adj: -900\n shm_size: 512m\n tmpfs:\n - /tmp:size=512m,mode=1777\n - /var/tmp:size=512m,mode=1777\n - /run/bdag-ephemeral:size=512m,mode=1777\nx-ops-low:\n blkio_config:\n weight: 100\n cpu_shares: 256\n oom_score_adj: 300\nx-pool-critical:\n blkio_config:\n weight: 900\n cpu_shares: 3072\n oom_score_adj: -800", + "repo_state": { + "blockdag-corechain": { + "branch": "codex/native-safe-mining-20260620-corechain", + "diff_stat": "", + "head": "7426d7fa1b39a29b57b5e81f24dc2baaede0c54c", + "last_commit": "7426d7f Protect mining health from zero coinbase templates", + "path": "/home/jeremy/blockdag-source/blockdag-corechain", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-corechain: codex post-savepoint zero-coinbase corechain patchset 20260621", + "status": "## codex/native-safe-mining-20260620-corechain...origin/codex/native-safe-mining-20260620-corechain [ahead 1]" + }, + "pool": { + "branch": "codex/native-safe-mining-20260620-pool", + "diff_stat": "", + "head": "937dff6d0cc248b35d38dbc681f3fe26a4ed6485", + "last_commit": "937dff6 Preserve safe ASIC jobs during transient node sync", + "path": "/home/jeremy/blockdag-source/pool", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-pool: codex post-savepoint zero-coinbase pool patchset 20260621", + "status": "## codex/native-safe-mining-20260620-pool...origin/codex/native-safe-mining-20260620-pool [ahead 2]" + }, + "redis-dash": { + "branch": "codex/native-safe-mining-20260620-dashboard", + "diff_stat": "", + "head": "7f3783333eddedaed162a04c4f3f70e5860bff23", + "last_commit": "7f37833 Adopt sidebar mining dashboard layout", + "path": "/home/jeremy/blockdag-source/redis-dash", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-dashboard: codex post-savepoint dashboard safety patchset 20260621", + "status": "## codex/native-safe-mining-20260620-dashboard...origin/codex/native-safe-mining-20260620-dashboard [ahead 2]" + }, + "stack-redis": { + "branch": "codex/native-safe-mining-20260620-stack", + "diff_stat": "", + "head": "1f7587ace24e0113a3a8934420a4f9f4b27b25ad", + "last_commit": "1f7587a Record sidebar pool deploy savepoints", + "path": "/home/jeremy/blockdag-source/stack-redis", + "stash_top": "stash@{0}: On codex/native-safe-mining-20260620-stack: codex post-savepoint zero-coinbase/watchdog patchset 20260621", + "status": "## codex/native-safe-mining-20260620-stack...origin/codex/native-safe-mining-20260620-stack [ahead 3]" + } + }, + "rollback_commands": [ + "docker tag stack-dashboard:safepoint-20260622-061448 stack-dashboard:latest && docker compose up -d --no-deps --no-build dashboard", + "docker compose stop pool && docker tag stack-pool:safepoint-20260622-061448 stack-pool:latest && docker compose up -d --no-deps --no-build pool", + "docker tag stack-status-sampler:safepoint-20260622-061448 stack-status-sampler:latest && docker compose up -d --no-deps --no-build status-sampler", + "If node rollback ever becomes necessary: stop pool first, restore node tag/config deliberately, verify native health and fresh peers, then start pool." + ], + "rollback_tags": { + "dashboard": "stack-dashboard:safepoint-20260622-061448", + "node": "stack-node:safepoint-20260622-061448", + "pool": "stack-pool:safepoint-20260622-061448", + "status_sampler": "stack-status-sampler:safepoint-20260622-061448" + }, + "savepoint_id": "20260622-061448-dashboard-control-suite-baseline-retained" +} diff --git a/ops/savepoints/20260622-063816-pre-control-suite-dashboard-deploy-retained.json b/ops/savepoints/20260622-063816-pre-control-suite-dashboard-deploy-retained.json new file mode 100644 index 0000000..4646328 --- /dev/null +++ b/ops/savepoints/20260622-063816-pre-control-suite-dashboard-deploy-retained.json @@ -0,0 +1,52 @@ +{ + "name": "pre-control-suite-dashboard-deploy-retained", + "created_at_local": "2026-06-22T06:38:16+02:00", + "created_at_utc": "2026-06-22T04:38:16Z", + "retained": true, + "reason": "Rollback point before deploying redis-dash read-only ASIC, Stratum Runtime, and Chain DAG dashboard telemetry.", + "repos": { + "stack_redis": { + "path": "/home/jeremy/blockdag-source/stack-redis", + "head": "3a0f89a", + "branch": "codex/native-safe-mining-20260620-stack" + }, + "redis_dash": { + "path": "/home/jeremy/blockdag-source/redis-dash", + "head": "0f983bc", + "branch": "codex/native-safe-mining-20260620-dashboard" + } + }, + "live_dashboard_before_deploy": { + "image": "stack-dashboard", + "image_id": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "rollback_tag": "stack-dashboard:pre-control-suite-deploy-20260622-063816", + "status": "Up 5 hours (healthy)", + "published": "127.0.0.1:8088->8088/tcp" + }, + "mining_gate_before_deploy": { + "dashboard_samples": 5, + "mode": "mining", + "overall": "ok", + "can_accept_shares": true, + "can_submit_blocks": true, + "ready_lanes": 4, + "sync_status": "synced", + "fresh_peer_count": 16, + "direct_pool_job_state": "ok", + "direct_pool_ready_connections": 4 + }, + "p2p_guard": { + "command": "standard stack-redis forbidden-module grep guard", + "result": "no hits" + }, + "rollback_commands": [ + "cd /home/jeremy/blockdag-source/stack-redis", + "docker image tag stack-dashboard:pre-control-suite-deploy-20260622-063816 stack-dashboard:latest", + "docker compose up -d --no-deps dashboard" + ], + "notes": [ + "Only dashboard service is eligible for redeploy from this savepoint.", + "Do not restart node or pool for this dashboard telemetry change.", + "If dashboard health or live mining gates fail after deploy, rollback dashboard image first and reassess from this retained point." + ] +} diff --git a/ops/savepoints/20260622-065500-pre-control-suite-dashboard-deploy-recovered-retained.json b/ops/savepoints/20260622-065500-pre-control-suite-dashboard-deploy-recovered-retained.json new file mode 100644 index 0000000..fecc180 --- /dev/null +++ b/ops/savepoints/20260622-065500-pre-control-suite-dashboard-deploy-recovered-retained.json @@ -0,0 +1,5952 @@ +{ + "savepoint_id": "20260622-065500-pre-control-suite-dashboard-deploy-recovered-retained", + "created_at": "2026-06-22T06:55:04+02:00", + "retained": true, + "purpose": "Recovered live dashboard-only predeploy savepoint before installing redis-dash ASIC/pool/chain control suite.", + "repos": { + "stack_redis_head": "577d99c", + "redis_dash_head": "0f983bc" + }, + "images": { + "current_dashboard_image": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "candidate_dashboard_image": "sha256:272a4534b1e9372488805e7ec8ad79db44effaf285062b89c773aa120488d8a3", + "dashboard_rollback_tag": "stack-dashboard:pre-control-suite-deploy-recovered-20260622-065500", + "pool_image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "node_image": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845" + }, + "live_node": { + "scope": "do not change for dashboard-only deploy", + "command": "/usr/local/bin/blockdag-node --configfile=/run/bdag-ephemeral/node.conf --addpeer=/ip4/16.28.133.168/tcp/8150/p2p/16Uiu2HAm9UcTayJDSajjJYsWwVaN2qqGeczcs9kXse3dMdvGDRjz --addpeer=/ip4/13.140.165.186/tcp/8150/p2p/16Uiu2HAm4hHD7Ht5LJrLgaKXr7YP2RzHHjrrCLNt8zv8FQ9s3gBU --addpeer=/ip4/13.57.132.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus --addpeer=/ip4/18.142.70.83/tcp/8150/p2p/16Uiu2HAmBSdn2taoteYwLZJZkDm2iCwL6eQ4UaXYNBBtAwaBU18X --addpeer=/ip4/199.229.220.118/tcp/8151/p2p/16Uiu2HAmUtspBkyUNBG8pQLgj9DrKVgL6SKQzGBhVT9ZShNM1ops --addpeer=/ip4/204.236.141.121/tcp/8150/p2p/16Uiu2HAm5qppgbBCjVpTN5nKtjEU5KsSbs2gz4S9uPiSWVSGfNh1 --addpeer=/ip4/3.126.64.13/tcp/8152/p2p/16Uiu2HAmEFxRaBbbf3sRi43CCvMk5Y6zPkuGY9s4uRK2FKJVJkqo --addpeer=/ip4/52.8.54.60/tcp/8150/p2p/16Uiu2HAkzUWr4YUM7mAhLdCVZgFoV9He6aQzKTyWWrJ7SWQKRTN7 --addpeer=/ip4/54.151.18.92/tcp/8150/p2p/16Uiu2HAmVm91o1JXS2C7P6uTVuVX5empgnRtzuK4UaC8fHmePbKP --addpeer=/ip4/63.182.36.180/tcp/8150/p2p/16Uiu2HAmP8HsTF9ks8JjFamzT9JBZb3ymSiCJ8rkzXBZqYj4yKtP --addpeer=/ip4/169.0.254.182/tcp/8150/p2p/16Uiu2HAmGsEDGnDGhvziZR1UQSELTDihXRKYMB6SrbEQ6NsWBVqD --addpeer=/ip4/102.39.221.179/tcp/8152/p2p/16Uiu2HAm8ebvL9NTNwgLYwm8LEEXSY7QKQhQ7YVcAV7PR6ymsrTm --addpeer=/ip4/80.141.136.179/tcp/8151/p2p/16Uiu2HAmMZNeDNLgfLXFKzEiWZHFjdDejLSCzhunJMYWoYhdT3gv --addpeer=/ip4/102.39.221.179/tcp/8150/p2p/16Uiu2HAmNQznwWPjuAUhXnTkP6EuWsEHa4eRnb1aTm3WcZQKsrFn --addpeer=/ip4/47.156.6.142/tcp/8151/p2p/16Uiu2HAmJz7PTCxSWTnuJhvbf4oXEffFZ1KAE3yNBX9SY72jS1ta --addpeer=/ip4/218.32.84.82/tcp/8150/p2p/16Uiu2HAmNjFKhP3945Az3afdBSUSRwhgDw1uGRBDsqSvSduStbWJ --addpeer=/ip4/5.78.193.245/tcp/8150/p2p/16Uiu2HAkwosZsQdtgAQHoJ57MFKpWnyfGJtSpt2R2aJMgR6ywGME --addpeer=/ip4/3.218.54.171/tcp/8150/p2p/16Uiu2HAmUZBdpuutiLPuru1UQnX8hf6GyvG7yU2B832sYjvNhmQG --addpeer=/ip4/102.182.77.21/tcp/8151/p2p/16Uiu2HAm2uPLqM1dfd3ddbzg3FDAmvuvyo3vbBSXpVHurqAsUxWQ --addpeer=/ip4/121.91.173.235/tcp/8150/p2p/16Uiu2HAkvH1U2kQqQFh6fSVQPGgABaKNHRMT7DceGe6kRtYr6ndY --addpeer=/ip4/102.182.77.16/tcp/8152/p2p/16Uiu2HAm99a8KYuUkL5LGEQbpRNhz5nzScrwYpEh47BfSaVMCg5G --addpeer=/ip4/54.214.229.250/tcp/8154/p2p/16Uiu2HAm1vZgJXKhUfWWC7nz9wzNpUDiYAid7zCZSKb1hhU7QBNB --addpeer=/ip4/54.201.253.248/tcp/8150/p2p/16Uiu2HAm5bpb5ssgN47BT4ChQjmbpGA2zAPJskWGMPknrFwaEtE9 --modules=Blockdag --modules=miner --miner --miningaddr=0x1719E0ee598c15957448D5E568948101DF78e7A0 --obsoleteheight=20 --debuglevel=debug --nofilelogging ", + "safety": "Blockdag and miner modules only with explicit mining address" + }, + "live_gates": { + "node_template_health": { + "checked_at_unix_ms": 1782104104190, + "enabled": true, + "started": true, + "shutdown": false, + "reason_code": "node_syncing", + "reason": "node is still syncing with peers", + "mineable_now": false, + "submit_ready": false, + "get_block_template_ready": false, + "get_block_template_reason_code": "node_syncing", + "get_block_template_reason": "node is still syncing with peers", + "worker_ready": true, + "worker_type": "GBT_Worker", + "worker_running": true, + "template_available": true, + "template_usable": false, + "template_age_ms": 7668, + "template_max_age_ms": 60000, + "template_timestamp_unix": 1782104095, + "latest_template_seq": 23553, + "template_height": 9367065, + "template_parent_root": "0x6e2ea01020082b5b4b5549b59b942961166b2986195b44491273d002066a2be3", + "template_tx_count": 0, + "template_coinbase_address": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "template_coinbase_valid": true, + "last_good_template_available": true, + "last_good_template_age_ms": 7668, + "last_template_build_at_unix_ms": 1782104096509, + "last_template_build_age_ms": 7680, + "last_template_build_duration_ms": 12, + "last_template_build_reason": "parent_changed", + "last_template_build_error_blocking": false, + "last_template_invalidation_at_unix_ms": 1782104096509, + "last_template_invalidation_seq": 28572, + "last_template_invalidation_cause": "parent_changed", + "last_template_invalidation_reason": "parent_changed", + "pending_template_build": false, + "p2p_current": true, + "p2p_nearly_synced": false, + "p2p_freshness_max_age_ms": 3000, + "p2p_peer_lead_tolerance_blocks": 10, + "p2p_consensus_peer_count": 23, + "p2p_fresh_consensus_peer_count": 1, + "p2p_stale_consensus_peer_count": 22, + "p2p_best_peer_main_order": 12233845, + "p2p_best_peer_lead_blocks": 0, + "p2p_best_peer_graph_state_age_ms": 512, + "p2p_avg_graph_state_age_ms": 3898354, + "p2p_max_graph_state_age_ms": 23198529, + "p2p_sync_peer_present": false, + "p2p_sync_peer_fresh": false, + "p2p_sync_peer_graph_state_age_ms": 0, + "p2p_mining_fresh": false, + "p2p_mining_fresh_reason_code": "not_enough_fresh_consensus_peers", + "sync_allowed": false, + "sync_reason_code": "node_syncing", + "sync_reason": "node is still syncing with peers", + "chain_current": true, + "main_order": 12233845, + "self_add": 0, + "submit_no_synced": false, + "nearly_sync_mining": false + }, + "pool_job_state": { + "generated_at_unix_ms": 1782104104198, + "status": "degraded", + "reason_code": "invalidated_current_job", + "active_connections": 4, + "authorized_connections": 4, + "subscribed_connections": 4, + "ready_connections": 0, + "connections_without_current_job": 0, + "stale_current_job_connections": 0, + "invalid_current_job_connections": 4, + "expired_current_job_connections": 0, + "current_template_seq": 28731, + "current_parent": "18bb811a57c5258769258ec364b2e5cd8e372e2bd128e99fd4320d417f62f962", + "last_broadcast_age_ms": 21475, + "max_current_job_age_ms": 21475, + "accepted_jobs": { + "total": 1626, + "stale": 518, + "invalidated": 1626, + "submitted": 128, + "expired": 1370 + }, + "expired_job": { + "enabled": true, + "window_ms": 10000, + "window_age_ms": 9051, + "window_rejects": 7, + "window_clients": 3, + "client_resend_threshold": 3, + "client_reconnect_threshold": 3, + "client_reconnect_window_ms": 120000, + "client_reconnect_cooldown_ms": 60000, + "global_clean_refresh_threshold": 100, + "global_clean_refresh_min_clients": 2, + "cooldown_ms": 15000, + "cooldown_active": false + }, + "submit_stall": { + "enabled": true, + "window_ms": 60000, + "window_age_ms": 29936, + "min_age_ms": 30000, + "failures": 3, + "stale_failures": 0, + "duplicate_failures": 0, + "failure_threshold": 8, + "stale_threshold": 3, + "duplicate_threshold": 6, + "cooldown_ms": 20000, + "last_recovery_age_ms": 4701267, + "cooldown_active": false, + "reason_code": "ok" + }, + "template_conversion_stall": { + "enabled": true, + "window_ms": 120000, + "window_age_ms": 3818, + "min_age_ms": 30000, + "accepted": 0, + "failures": 1, + "stale_failures": 0, + "duplicate_failures": 0, + "total_candidates": 1, + "failure_ratio": 100, + "failure_ratio_threshold": 85, + "min_candidates": 40, + "active_miners": 4, + "min_active_miners": 1, + "cooldown_ms": 30000, + "last_recovery_age_ms": 712521, + "cooldown_active": false, + "last_reason": "stale-job", + "reason_code": "warming_up" + }, + "clients": [ + { + "address": "192.168.1.101:39615", + "remote_host": "192.168.1.101", + "asic_mac": "28:e2:97:1e:c0:b5", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "lane_identity_source": "mac", + "authorized": true, + "subscribed": true, + "extranonce_subscribed": false, + "ready": false, + "reason_code": "invalidated_current_job", + "current_job_id": "28731-18bb4e38a373cc5c_39000000", + "current_job_age_ms": 21475, + "template_seq": 28731, + "backend": "node", + "parent": "18bb811a57c5258769258ec364b2e5cd8e372e2bd128e99fd4320d417f62f962", + "pdiff": 0.13635887461147622, + "expired_rejects": 7, + "expired_window_rejects": 0, + "low_diff_rejects": 0 + }, + { + "address": "192.168.1.14:40488", + "remote_host": "192.168.1.14", + "asic_mac": "28:e2:97:3e:39:63", + "lane_id": "mac:28:e2:97:3e:39:63", + "lane_identity_source": "mac", + "authorized": true, + "subscribed": true, + "extranonce_subscribed": false, + "ready": false, + "reason_code": "invalidated_current_job", + "current_job_id": "28731-18bb4e38a373cc5c_3c000000", + "current_job_age_ms": 21475, + "template_seq": 28731, + "backend": "node", + "parent": "18bb811a57c5258769258ec364b2e5cd8e372e2bd128e99fd4320d417f62f962", + "pdiff": 0.14559958533516773, + "expired_rejects": 3, + "expired_window_rejects": 3, + "low_diff_rejects": 0 + }, + { + "address": "192.168.1.105:51994", + "remote_host": "192.168.1.105", + "asic_mac": "28:e2:97:4d:44:3a", + "lane_id": "mac:28:e2:97:4d:44:3a", + "lane_identity_source": "mac", + "authorized": true, + "subscribed": true, + "extranonce_subscribed": false, + "ready": false, + "reason_code": "invalidated_current_job", + "current_job_id": "28731-18bb4e38a373cc5c_3b000000", + "current_job_age_ms": 21475, + "template_seq": 28731, + "backend": "node", + "parent": "18bb811a57c5258769258ec364b2e5cd8e372e2bd128e99fd4320d417f62f962", + "pdiff": 0.11409061883263331, + "expired_rejects": 4, + "expired_window_rejects": 2, + "low_diff_rejects": 0 + }, + { + "address": "192.168.1.102:48385", + "remote_host": "192.168.1.102", + "asic_mac": "2a:71:c7:f5:1f:1e", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "lane_identity_source": "mac", + "authorized": true, + "subscribed": true, + "extranonce_subscribed": false, + "ready": false, + "reason_code": "invalidated_current_job", + "current_job_id": "28731-18bb4e38a373cc5c_32000000", + "current_job_age_ms": 21475, + "template_seq": 28731, + "backend": "node", + "parent": "18bb811a57c5258769258ec364b2e5cd8e372e2bd128e99fd4320d417f62f962", + "pdiff": 0.14232223787390932, + "expired_rejects": 4, + "expired_window_rejects": 2, + "low_diff_rejects": 0 + } + ], + "router": { + "enabled": true, + "node_name": "node", + "node": { + "name": "node", + "selected": true, + "healthy": false, + "reason": "submit-readiness-template-refresh-node-syncing", + "score": 0, + "last_template_age_ms": 7676, + "last_template_height": 9367065, + "last_template_seq": 23553, + "ws_connected": true, + "recent_submit_errors": 2, + "last_submit_age_ms": 21073, + "last_submit_error": "structured submit rejected: code=node_syncing message=Client in initial download : Blockdag is downloading blocks: node is still syncing with peers" + } + } + }, + "dashboard_status": { + "asic_performance": { + "age_seconds": 1, + "bucket_seconds": 600, + "generated_at_unix_ms": 1782104103975, + "lanes": [ + { + "asic_mac": "28:e2:97:1e:c0:b5", + "blocks_1h": 323, + "blocks_24h": 2437, + "blocks_30m": 113, + "blocks_6h": 2437, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 62, + "rejected": 0, + "rejected_local": 3, + "total": 65 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 88, + "rejected": 1, + "rejected_local": 7, + "total": 96 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 5, + "total": 83 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 62, + "rejected": 1, + "rejected_local": 2, + "total": 65 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 91, + "rejected": 0, + "rejected_local": 6, + "total": 97 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 90, + "rejected": 0, + "rejected_local": 3, + "total": 93 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 85, + "rejected": 0, + "rejected_local": 3, + "total": 88 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 63, + "rejected": 3, + "rejected_local": 2, + "total": 68 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 73, + "rejected": 2, + "rejected_local": 5, + "total": 80 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 7, + "total": 79 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 89, + "rejected": 0, + "rejected_local": 6, + "total": 95 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 99, + "rejected": 2, + "rejected_local": 9, + "total": 110 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 58, + "rejected": 0, + "rejected_local": 3, + "total": 61 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 69, + "rejected": 2, + "rejected_local": 6, + "total": 77 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 93, + "rejected": 0, + "rejected_local": 6, + "total": 99 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 92, + "rejected": 0, + "rejected_local": 3, + "total": 95 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 3, + "total": 85 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 88, + "rejected": 0, + "rejected_local": 8, + "total": 96 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 72, + "rejected": 1, + "rejected_local": 7, + "total": 80 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 88, + "rejected": 2, + "rejected_local": 5, + "total": 95 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 68, + "rejected": 2, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 74, + "rejected": 4, + "rejected_local": 7, + "total": 85 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 4, + "total": 75 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 68, + "rejected": 10, + "rejected_local": 2, + "total": 80 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 88, + "rejected": 0, + "rejected_local": 3, + "total": 91 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 5, + "total": 87 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 1, + "total": 81 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 63, + "rejected": 0, + "rejected_local": 7, + "total": 70 + }, + "start_unix_ms": 1782101400000 + }, + { + "counts": { + "accepted": 67, + "rejected": 0, + "rejected_local": 2, + "total": 69 + }, + "start_unix_ms": 1782102000000 + }, + { + "counts": { + "accepted": 77, + "rejected": 2, + "rejected_local": 4, + "total": 83 + }, + "start_unix_ms": 1782102600000 + }, + { + "counts": { + "accepted": 4, + "rejected": 13, + "rejected_local": 3, + "total": 20 + }, + "start_unix_ms": 1782103200000 + }, + { + "counts": { + "accepted": 32, + "rejected": 1, + "rejected_local": 3, + "total": 36 + }, + "start_unix_ms": 1782103800000 + } + ], + "client_addr": "192.168.1.101:39615", + "current_job_age_ms": 21253, + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782104073821, + "last_event_unix_ms": 1782104100378, + "pdiff": 0.13635887461147622, + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": 16, + "rejected_local_30m": 10, + "remote_host": "192.168.1.101", + "totals": { + "accepted": 2437, + "rejected": 47, + "rejected_local": 144, + "total": 2628 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:3e:39:63", + "blocks_1h": 322, + "blocks_24h": 2100, + "blocks_30m": 108, + "blocks_6h": 2100, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 57, + "rejected": 0, + "rejected_local": 3, + "total": 60 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 79, + "rejected": 0, + "rejected_local": 11, + "total": 90 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 2, + "total": 75 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 5, + "total": 83 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 6, + "total": 78 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 63, + "rejected": 1, + "rejected_local": 4, + "total": 68 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 3, + "total": 76 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 82, + "rejected": 2, + "rejected_local": 3, + "total": 87 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 81, + "rejected": 4, + "rejected_local": 19, + "total": 104 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 66, + "rejected": 0, + "rejected_local": 5, + "total": 71 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 74, + "rejected": 0, + "rejected_local": 1, + "total": 75 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 64, + "rejected": 1, + "rejected_local": 5, + "total": 70 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 66, + "rejected": 0, + "rejected_local": 4, + "total": 70 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 54, + "rejected": 4, + "rejected_local": 0, + "total": 58 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 7, + "total": 89 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 5, + "total": 75 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 28, + "rejected": 1, + "rejected_local": 2, + "total": 31 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 57, + "rejected": 0, + "rejected_local": 3, + "total": 60 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 59, + "rejected": 3, + "rejected_local": 8, + "total": 70 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 60, + "rejected": 1, + "rejected_local": 2, + "total": 63 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 72, + "rejected": 3, + "rejected_local": 7, + "total": 82 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 79, + "rejected": 1, + "rejected_local": 5, + "total": 85 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 2, + "total": 84 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 67, + "rejected": 7, + "rejected_local": 2, + "total": 76 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 73, + "rejected": 3, + "rejected_local": 4, + "total": 80 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 68, + "rejected": 0, + "rejected_local": 5, + "total": 73 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 59, + "rejected": 0, + "rejected_local": 2, + "total": 61 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 84, + "rejected": 1, + "rejected_local": 7, + "total": 92 + }, + "start_unix_ms": 1782101400000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 5, + "total": 76 + }, + "start_unix_ms": 1782102000000 + }, + { + "counts": { + "accepted": 74, + "rejected": 1, + "rejected_local": 5, + "total": 80 + }, + "start_unix_ms": 1782102600000 + }, + { + "counts": { + "accepted": 3, + "rejected": 12, + "rejected_local": 9, + "total": 24 + }, + "start_unix_ms": 1782103200000 + }, + { + "counts": { + "accepted": 31, + "rejected": 0, + "rejected_local": 2, + "total": 33 + }, + "start_unix_ms": 1782103800000 + } + ], + "client_addr": "192.168.1.14:40488", + "current_job_age_ms": 21253, + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "lane_id": "mac:28:e2:97:3e:39:63", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782104074155, + "last_event_unix_ms": 1782104074155, + "pdiff": 0.14559958533516773, + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": 13, + "rejected_local_30m": 16, + "remote_host": "192.168.1.14", + "totals": { + "accepted": 2100, + "rejected": 46, + "rejected_local": 153, + "total": 2299 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "28:e2:97:4d:44:3a", + "blocks_1h": 293, + "blocks_24h": 2198, + "blocks_30m": 99, + "blocks_6h": 2198, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 54, + "rejected": 0, + "rejected_local": 2, + "total": 56 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 86, + "rejected": 0, + "rejected_local": 7, + "total": 93 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 81, + "rejected": 0, + "rejected_local": 3, + "total": 84 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 68, + "rejected": 1, + "rejected_local": 2, + "total": 71 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 92, + "rejected": 0, + "rejected_local": 6, + "total": 98 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 67, + "rejected": 2, + "rejected_local": 5, + "total": 74 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 5, + "total": 85 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 61, + "rejected": 3, + "rejected_local": 5, + "total": 69 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 72, + "rejected": 3, + "rejected_local": 6, + "total": 81 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 66, + "rejected": 1, + "rejected_local": 4, + "total": 71 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 77, + "rejected": 0, + "rejected_local": 5, + "total": 82 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 88, + "rejected": 1, + "rejected_local": 9, + "total": 98 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 3, + "total": 73 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 59, + "rejected": 2, + "rejected_local": 4, + "total": 65 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 71, + "rejected": 1, + "rejected_local": 10, + "total": 82 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 63, + "rejected": 0, + "rejected_local": 5, + "total": 68 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 1, + "rejected_local": 3, + "total": 75 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 3, + "rejected_local": 5, + "total": 78 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 67, + "rejected": 1, + "rejected_local": 7, + "total": 75 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 73, + "rejected": 3, + "rejected_local": 3, + "total": 79 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 68, + "rejected": 0, + "rejected_local": 3, + "total": 71 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 76, + "rejected": 3, + "rejected_local": 6, + "total": 85 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 58, + "rejected": 1, + "rejected_local": 0, + "total": 59 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 64, + "rejected": 0, + "rejected_local": 3, + "total": 67 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 68, + "rejected": 7, + "rejected_local": 2, + "total": 77 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 64, + "rejected": 1, + "rejected_local": 3, + "total": 68 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 2, + "total": 73 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 72, + "rejected": 0, + "rejected_local": 4, + "total": 76 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 64, + "rejected": 2, + "rejected_local": 5, + "total": 71 + }, + "start_unix_ms": 1782101400000 + }, + { + "counts": { + "accepted": 58, + "rejected": 0, + "rejected_local": 3, + "total": 61 + }, + "start_unix_ms": 1782102000000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 4, + "total": 77 + }, + "start_unix_ms": 1782102600000 + }, + { + "counts": { + "accepted": 1, + "rejected": 9, + "rejected_local": 3, + "total": 13 + }, + "start_unix_ms": 1782103200000 + }, + { + "counts": { + "accepted": 25, + "rejected": 1, + "rejected_local": 1, + "total": 27 + }, + "start_unix_ms": 1782103800000 + } + ], + "client_addr": "192.168.1.105:51994", + "current_job_age_ms": 21253, + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "lane_id": "mac:28:e2:97:4d:44:3a", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782104058746, + "last_event_unix_ms": 1782104083029, + "pdiff": 0.11409061883263331, + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": 10, + "rejected_local_30m": 8, + "remote_host": "192.168.1.105", + "totals": { + "accepted": 2198, + "rejected": 46, + "rejected_local": 138, + "total": 2382 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + }, + { + "asic_mac": "2a:71:c7:f5:1f:1e", + "blocks_1h": 346, + "blocks_24h": 2360, + "blocks_30m": 116, + "blocks_6h": 2360, + "buckets": [ + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782017400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782018600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782019800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782020400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782021600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782022800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782023400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782024600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782025800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782026400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782027600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782028800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782029400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782030600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782031800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782032400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782033600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782034800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782035400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782036600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782037800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782038400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782039600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782040800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782041400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782042600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782043800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782044400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782045600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782046800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782047400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782048600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782049800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782050400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782051600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782052800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782053400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782054600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782055800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782056400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782057600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782058800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782059400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782060600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782061800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782062400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782063600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782064800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782065400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782066600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782067800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782068400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782069600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782070800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782071400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782072600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782073800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782074400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782075600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782076800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782077400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782078600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782079800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782080400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081000000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782081600000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082200000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782082800000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782083400000 + }, + { + "counts": { + "accepted": 0, + "rejected": 0, + "rejected_local": 0, + "total": 0 + }, + "start_unix_ms": 1782084000000 + }, + { + "counts": { + "accepted": 44, + "rejected": 0, + "rejected_local": 1, + "total": 45 + }, + "start_unix_ms": 1782084600000 + }, + { + "counts": { + "accepted": 77, + "rejected": 3, + "rejected_local": 13, + "total": 93 + }, + "start_unix_ms": 1782085200000 + }, + { + "counts": { + "accepted": 75, + "rejected": 0, + "rejected_local": 6, + "total": 81 + }, + "start_unix_ms": 1782085800000 + }, + { + "counts": { + "accepted": 96, + "rejected": 1, + "rejected_local": 5, + "total": 102 + }, + "start_unix_ms": 1782086400000 + }, + { + "counts": { + "accepted": 73, + "rejected": 2, + "rejected_local": 7, + "total": 82 + }, + "start_unix_ms": 1782087000000 + }, + { + "counts": { + "accepted": 62, + "rejected": 0, + "rejected_local": 7, + "total": 69 + }, + "start_unix_ms": 1782087600000 + }, + { + "counts": { + "accepted": 77, + "rejected": 0, + "rejected_local": 3, + "total": 80 + }, + "start_unix_ms": 1782088200000 + }, + { + "counts": { + "accepted": 65, + "rejected": 1, + "rejected_local": 2, + "total": 68 + }, + "start_unix_ms": 1782088800000 + }, + { + "counts": { + "accepted": 89, + "rejected": 2, + "rejected_local": 14, + "total": 105 + }, + "start_unix_ms": 1782089400000 + }, + { + "counts": { + "accepted": 95, + "rejected": 0, + "rejected_local": 4, + "total": 99 + }, + "start_unix_ms": 1782090000000 + }, + { + "counts": { + "accepted": 80, + "rejected": 0, + "rejected_local": 3, + "total": 83 + }, + "start_unix_ms": 1782090600000 + }, + { + "counts": { + "accepted": 67, + "rejected": 1, + "rejected_local": 6, + "total": 74 + }, + "start_unix_ms": 1782091200000 + }, + { + "counts": { + "accepted": 70, + "rejected": 0, + "rejected_local": 6, + "total": 76 + }, + "start_unix_ms": 1782091800000 + }, + { + "counts": { + "accepted": 82, + "rejected": 2, + "rejected_local": 5, + "total": 89 + }, + "start_unix_ms": 1782092400000 + }, + { + "counts": { + "accepted": 90, + "rejected": 0, + "rejected_local": 5, + "total": 95 + }, + "start_unix_ms": 1782093000000 + }, + { + "counts": { + "accepted": 79, + "rejected": 0, + "rejected_local": 11, + "total": 90 + }, + "start_unix_ms": 1782093600000 + }, + { + "counts": { + "accepted": 83, + "rejected": 1, + "rejected_local": 4, + "total": 88 + }, + "start_unix_ms": 1782094200000 + }, + { + "counts": { + "accepted": 82, + "rejected": 0, + "rejected_local": 6, + "total": 88 + }, + "start_unix_ms": 1782094800000 + }, + { + "counts": { + "accepted": 75, + "rejected": 0, + "rejected_local": 3, + "total": 78 + }, + "start_unix_ms": 1782095400000 + }, + { + "counts": { + "accepted": 69, + "rejected": 2, + "rejected_local": 4, + "total": 75 + }, + "start_unix_ms": 1782096000000 + }, + { + "counts": { + "accepted": 69, + "rejected": 1, + "rejected_local": 2, + "total": 72 + }, + "start_unix_ms": 1782096600000 + }, + { + "counts": { + "accepted": 70, + "rejected": 2, + "rejected_local": 9, + "total": 81 + }, + "start_unix_ms": 1782097200000 + }, + { + "counts": { + "accepted": 61, + "rejected": 0, + "rejected_local": 3, + "total": 64 + }, + "start_unix_ms": 1782097800000 + }, + { + "counts": { + "accepted": 78, + "rejected": 0, + "rejected_local": 1, + "total": 79 + }, + "start_unix_ms": 1782098400000 + }, + { + "counts": { + "accepted": 65, + "rejected": 4, + "rejected_local": 4, + "total": 73 + }, + "start_unix_ms": 1782099000000 + }, + { + "counts": { + "accepted": 70, + "rejected": 2, + "rejected_local": 3, + "total": 75 + }, + "start_unix_ms": 1782099600000 + }, + { + "counts": { + "accepted": 71, + "rejected": 0, + "rejected_local": 3, + "total": 74 + }, + "start_unix_ms": 1782100200000 + }, + { + "counts": { + "accepted": 73, + "rejected": 0, + "rejected_local": 3, + "total": 76 + }, + "start_unix_ms": 1782100800000 + }, + { + "counts": { + "accepted": 68, + "rejected": 1, + "rejected_local": 4, + "total": 73 + }, + "start_unix_ms": 1782101400000 + }, + { + "counts": { + "accepted": 89, + "rejected": 0, + "rejected_local": 5, + "total": 94 + }, + "start_unix_ms": 1782102000000 + }, + { + "counts": { + "accepted": 77, + "rejected": 3, + "rejected_local": 3, + "total": 83 + }, + "start_unix_ms": 1782102600000 + }, + { + "counts": { + "accepted": 2, + "rejected": 10, + "rejected_local": 4, + "total": 16 + }, + "start_unix_ms": 1782103200000 + }, + { + "counts": { + "accepted": 37, + "rejected": 0, + "rejected_local": 0, + "total": 37 + }, + "start_unix_ms": 1782103800000 + } + ], + "client_addr": "192.168.1.102:48385", + "current_job_age_ms": 21253, + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "lane_identity_source": "mac", + "last_accepted_unix_ms": 1782104069933, + "last_event_unix_ms": 1782104069933, + "pdiff": 0.14232223787390932, + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": 13, + "rejected_local_30m": 7, + "remote_host": "192.168.1.102", + "totals": { + "accepted": 2360, + "rejected": 38, + "rejected_local": 159, + "total": 2557 + }, + "worker": "0x1719E0ee598c15957448D5E568948101DF78e7A0" + } + ], + "last_update_age_seconds": 1, + "source": "pool-health-asic-performance", + "updated_at": "2026-06-22T04:55:03Z", + "updated_at_epoch": 1782104103, + "window_seconds": 86400 + }, + "can_accept_shares": true, + "can_submit_blocks": false, + "chain_block_count": "12233846", + "containers": { + "node": { + "running": true + }, + "pool": { + "running": true + } + }, + "miner_health": { + "configured_count": 4, + "connected_count": 0, + "identity_contract": "mac-only", + "managed_count": 4, + "managed_miners": [ + { + "blocks_1h": "323", + "blocks_24h": "2437", + "blocks_30m": "113", + "blocks_6h": "2437", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782104073821", + "last_event_at_epoch_ms": "1782104100378", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.13635887461147622", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "16", + "rejected_local_30m": "10", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "346", + "blocks_24h": "2360", + "blocks_30m": "116", + "blocks_6h": "2360", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782104069933", + "last_event_at_epoch_ms": "1782104069933", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.14232223787390932", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "13", + "rejected_local_30m": "7", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "293", + "blocks_24h": "2198", + "blocks_30m": "99", + "blocks_6h": "2198", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782104058746", + "last_event_at_epoch_ms": "1782104083029", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.11409061883263331", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "10", + "rejected_local_30m": "8", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "322", + "blocks_24h": "2100", + "blocks_30m": "108", + "blocks_6h": "2100", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782104074155", + "last_event_at_epoch_ms": "1782104074155", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.14559958533516773", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "13", + "rejected_local_30m": "16", + "source": "env-overrides", + "status": "invalidated_current_job" + } + ], + "miners": [ + { + "blocks_1h": "323", + "blocks_24h": "2437", + "blocks_30m": "113", + "blocks_6h": "2437", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:1e:c0:b5", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.101", + "lane_id": "mac:28:e2:97:1e:c0:b5", + "last_block_at_epoch_ms": "1782104073821", + "last_event_at_epoch_ms": "1782104100378", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:1e:c0:b5", + "managed": true, + "pdiff": "0.13635887461147622", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "16", + "rejected_local_30m": "10", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "346", + "blocks_24h": "2360", + "blocks_30m": "116", + "blocks_6h": "2360", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "2a:71:c7:f5:1f:1e", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.102", + "lane_id": "mac:2a:71:c7:f5:1f:1e", + "last_block_at_epoch_ms": "1782104069933", + "last_event_at_epoch_ms": "1782104069933", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "2a:71:c7:f5:1f:1e", + "managed": true, + "pdiff": "0.14232223787390932", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "13", + "rejected_local_30m": "7", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "293", + "blocks_24h": "2198", + "blocks_30m": "99", + "blocks_6h": "2198", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:4d:44:3a", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.105", + "lane_id": "mac:28:e2:97:4d:44:3a", + "last_block_at_epoch_ms": "1782104058746", + "last_event_at_epoch_ms": "1782104083029", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:4d:44:3a", + "managed": true, + "pdiff": "0.11409061883263331", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "10", + "rejected_local_30m": "8", + "source": "env-overrides", + "status": "invalidated_current_job" + }, + { + "blocks_1h": "322", + "blocks_24h": "2100", + "blocks_30m": "108", + "blocks_6h": "2100", + "configured_pool_url": "stratum+tcp://192.168.1.100:3334", + "connected": false, + "current_job_age_ms": "21253", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "extranonce_subscribed": false, + "health": "invalidated_current_job", + "identity": "28:e2:97:3e:39:63", + "intended_wallet": "0x1719E0ee598c15957448D5E568948101DF78e7A0", + "ip": "192.168.1.14", + "lane_id": "mac:28:e2:97:3e:39:63", + "last_block_at_epoch_ms": "1782104074155", + "last_event_at_epoch_ms": "1782104074155", + "last_seen_at": "2026-06-22T04:54:43Z", + "mac": "28:e2:97:3e:39:63", + "managed": true, + "pdiff": "0.14559958533516773", + "protocol_variant": "stratum-v1-basic", + "ready": false, + "rejected_30m": "13", + "rejected_local_30m": "16", + "source": "env-overrides", + "status": "invalidated_current_job" + } + ], + "source": "redis-asic-status", + "source_key": "bdag:miners:by_mac", + "tracked_count": 4 + }, + "mode": "mining", + "overall": "degraded", + "pool_health": { + "accepted_block_count": "9095", + "block_submit_error_count": "177", + "block_submit_success_count": "9095", + "source_job_health": { + "authorized_miners": "4", + "ready_miners": "0" + }, + "stale_jobs": false, + "valid_share_count": "10043" + }, + "pool_metrics": { + "accepted_block_recent": true, + "accepted_block_submissions": 9095, + "accepted_block_submissions_delta": 0, + "accepted_block_submissions_rate_per_second": 0, + "active_connections": 4, + "authorized_miners": 4, + "backend_main_order": 12233840, + "backend_mineable": 0, + "backend_p2p_mining_fresh": 0, + "backend_peer_lead_blocks": 0, + "backend_submit_ready": 0, + "blocks_found_total": 9272, + "expired_miners": 0, + "invalidated_miners": 4, + "last_accepted_block_submission_age_seconds": 25, + "last_accepted_block_submission_at_epoch": 1782104078, + "ready_miners": 0, + "rejected_block_submissions": 177, + "shares_accepted_total": 10043, + "shares_rejected_total": 3656, + "stale_miners": 0, + "template_age_seconds": 3.468 + }, + "source": "redis-live", + "status": "ok", + "sync_progress": { + "best_peer_mainorder": 12233845, + "chain_block_count": 12233846, + "chain_rpc_source": "getBlockCount", + "chain_syncing": false, + "current_block": 12233846, + "eth_syncing": false, + "evm_block_count": "11864803", + "evm_chain_syncing": false, + "evm_rpc_url": "http://host.docker.internal:18545", + "highest_block": 12233846, + "native_is_current": true, + "p2p_connections": 1, + "p2p_network_gap": 0, + "p2p_network_height": 12233845, + "peer_count": 1, + "peer_mainorder_gap": 0, + "remaining_blocks": 0, + "source": "native-rpc", + "status": "synced" + }, + "updated_at": "2026-06-22T04:55:04Z" + } + }, + "guard": { + "result": "standard forbidden module grep guard clean before deploy" + }, + "rollback_commands": [ + "cd /home/jeremy/blockdag-source/stack-redis", + "docker image tag stack-dashboard:pre-control-suite-deploy-recovered-20260622-065500 stack-dashboard:latest", + "docker compose up -d --no-deps --no-build dashboard", + "verify /api/status, /health/job-state, and 4 ready connections" + ], + "notes": [ + "Dashboard-only deploy candidate already built; node/pool must not be restarted by this savepoint.", + "If ready connections drop or dashboard health fails, rollback dashboard image first and reassess." + ] +} diff --git a/ops/savepoints/20260622-0735-pre-rebuild-redeploy-retained.json b/ops/savepoints/20260622-0735-pre-rebuild-redeploy-retained.json new file mode 100644 index 0000000..12077bc --- /dev/null +++ b/ops/savepoints/20260622-0735-pre-rebuild-redeploy-retained.json @@ -0,0 +1,105 @@ +{ + "savepoint_id": "20260622-0735-pre-rebuild-redeploy-retained", + "created_at": "2026-06-22T07:35:00+02:00", + "retained": true, + "purpose": "Rollback target before rebuilding and redeploying current pool and redis-dash source changes.", + "repos": { + "stack_redis_branch": "codex/native-safe-mining-20260620-stack", + "stack_redis_head": "efb6e26", + "pool_branch": "codex/native-safe-mining-20260620-pool", + "pool_head": "a890d5c", + "redis_dash_branch": "codex/native-safe-mining-20260620-dashboard", + "redis_dash_head": "915d5c2" + }, + "source_guards": { + "forbidden_peer_module_grep": "passed before this savepoint: no active peer-module hits in stack-redis live startup, examples, defaults, generated configs, tests, or compose paths", + "live_node_module_invariant": "Blockdag and miner modules only with explicit mining address; do not deploy node in this change" + }, + "rollback_tags": { + "pool": "stack-pool:20260622-0735-pre-rebuild-redeploy", + "dashboard": "stack-dashboard:20260622-0735-pre-rebuild-redeploy", + "node": "stack-node:20260622-0735-pre-rebuild-redeploy", + "status_sampler": "stack-status-sampler:20260622-0735-pre-rebuild-redeploy" + }, + "live_image_ids": { + "pool": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "dashboard_running": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "dashboard_latest_before_deploy": "sha256:272a4534b1e9372488805e7ec8ad79db44effaf285062b89c773aa120488d8a3", + "node": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "status_sampler": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739" + }, + "live_baseline": { + "dashboard_api": { + "overall": "ok", + "mode": "mining", + "can_accept_shares": true, + "can_submit_blocks": true, + "source": "redis-live", + "sync_status": "synced", + "native_is_current": true, + "p2p_connections": 5, + "p2p_network_gap": 0, + "ready_miners": 4, + "accepted_block_submissions": 10359, + "accepted_block_submissions_delta": 2, + "accepted_block_recent": true + }, + "pool_job_state": { + "status": "ok", + "reason_code": "ok", + "active_connections": 4, + "authorized_connections": 4, + "ready_connections": 4, + "stale_current_job_connections": 0, + "invalid_current_job_connections": 0, + "expired_current_job_connections": 0, + "current_template_seq": 32568, + "last_broadcast_age_ms": 1032, + "router_node_reason": "ok" + }, + "host_pressure": { + "node_memory": "4.352GiB / 15.31GiB", + "pool_memory": "37.58MiB / 15.31GiB", + "dashboard_memory": "57.97MiB / 15.31GiB" + } + }, + "rollback_commands": [ + "cd /home/jeremy/blockdag-source/stack-redis", + "docker image tag stack-dashboard:20260622-0735-pre-rebuild-redeploy stack-dashboard:latest && docker compose up -d --no-deps --no-build dashboard", + "docker compose stop pool && docker image tag stack-pool:20260622-0735-pre-rebuild-redeploy stack-pool:latest && docker compose up -d --no-deps --no-build pool", + "Node rollback should be avoided for this deploy; if required, stop pool first, tag stack-node:20260622-0735-pre-rebuild-redeploy as stack-node:latest, restore node deliberately, verify native health/fresh peers, then restart pool." + ], + "deployment_attempt": { + "attempted_at": "2026-06-22T07:37:00+02:00", + "candidate_images": { + "pool": "stack-pool:latest sha256:00f2570674171002557f5980332431263062534c03641ed8f728f9553cf3c9f5", + "dashboard": "stack-dashboard:latest sha256:7730e6213b1f49eba3f2ef4b8141acb7c03c1aae6f1fc3d722ebf0a7de6dec97" + }, + "dashboard_stage": { + "result": "passed", + "method": "docker compose up -d --no-deps --no-build dashboard", + "observation": "Dashboard health became healthy; pool stayed ok/ok with 4 ready lanes and accepted submissions advanced from 10471 to 10495 during the 60 second gate." + }, + "pool_stage": { + "result": "failed_safety_gate_and_rolled_back", + "method": "docker compose up -d --no-deps --no-build pool", + "unsafe_window": "From 2026-06-22T07:40:26+02:00 through rollback decision, pool job-state degraded to invalidated_current_job, ready_connections=0, dashboard degraded to node_syncing/mining, submit=false, backend P2P mining fresh=0, and accepted submissions stalled at 10.", + "rollback": "Tagged pre-rebuild pool and dashboard images back to latest and recreated both services. Node and chain data were not restarted or modified.", + "asic_recovery": "After rollback, ASIC 192.168.1.105 / MAC 28:e2:97:4d:44:3a repeatedly connected then closed before subscribe/authorize; /mcb/pools and cgminer port-80 reads timed out while /mcb/status and /mcb/setting responded. An authenticated restart of only that ASIC restored the fourth ready lane.", + "final_state": "At 2026-06-22T07:48:17+02:00, running images were back to pre-rebuild digests, dashboard was ok/mining, can_submit_blocks=true, native current=true, backend mineable=1, submit_ready=1, P2P mining fresh=1, pool job-state ok/ok with 4 active/authorized/ready lanes, and accepted submissions were advancing.", + "dashboard_projection_caveat": "After rollback, old dashboard /api/status could still briefly project ready_miners=0 and backend mineable/submit_ready=0 while direct pool /health/job-state and /metrics showed pool_job_health_ready_miners=4, backend mineable=1, submit_ready=1, and accepted submissions rising. Future deploy gates must verify direct pool/router metrics before acting on the rolled-back dashboard projection.", + "post_record_confirmation": "At the final 2026-06-22T07:56+02:00 check, direct pool API on 127.0.0.1:9090 stayed ok with 4 active/authorized/ready ASIC lanes, backend mineable=1, submit_ready=1, P2P mining fresh=1, and pool_block_submit_outcomes_total{outcome=accepted,reason=ok} increased from 425 to 436 over 30 seconds." + }, + "next_retry_requirement": "Do not retry pool deployment until the node_syncing/template-invalidation recovery path is fixed or the pool deploy protocol proves it can preserve or promptly refresh all ASIC jobs after a restart without entering sustained zero-ready invalidated state." + }, + "unsafe_signals_for_revert": [ + "p2p module appears live", + "node command/env diverges from Blockdag/miner-only invariant", + "node enters restart loop", + "dashboard health fails after dashboard-only deploy", + "pool ready miners drop and do not recover after sufficient observation", + "accepted submissions stop while ASICs have work", + "zero-coinbase or unsafe template reaches ASIC jobs", + "dashboard says ok while template/mining is unsafe" + ] +} diff --git a/ops/savepoints/20260622-0818-pre-morning-safe-build-retained.json b/ops/savepoints/20260622-0818-pre-morning-safe-build-retained.json new file mode 100644 index 0000000..ce301c5 --- /dev/null +++ b/ops/savepoints/20260622-0818-pre-morning-safe-build-retained.json @@ -0,0 +1,62 @@ +{ + "savepoint_id": "20260622-0818-pre-morning-safe-build-retained", + "created_at": "2026-06-22T08:18:00+02:00", + "retained": true, + "purpose": "Pre-build savepoint before producing a morning candidate that excludes the failed pool broadcast-transition change.", + "isolation_result": { + "failed_candidate_pool_image": "sha256:00f2570674171002557f5980332431263062534c03641ed8f728f9553cf3c9f5", + "known_good_pool_image": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "known_good_pool_source": "937dff6 Preserve safe ASIC jobs during transient node sync", + "failed_candidate_pool_source": "a890d5c Distinguish pool broadcast transition health", + "isolated_problem": "a890d5c was the only pool source delta between the known-good running image and the failed pool candidate. It is excluded from the morning build by pool revert commit 79ad00e.", + "release_decision": "Do not include a890d5c in the morning pool build. Keep the dashboard/display side optional, but do not ship the pool awaiting-current-job-broadcast health change until the restart/zero-ready path is redesigned and proven." + }, + "source_refs": { + "stack_redis_branch": "codex/native-safe-mining-20260620-stack", + "stack_redis_head": "b1818dd", + "pool_branch": "codex/native-safe-mining-20260620-pool", + "pool_head": "79ad00e", + "pool_head_note": "Reverts a890d5c; effective pool code matches the known-good 937dff6 behavior for the isolated files.", + "redis_dash_branch": "codex/native-safe-mining-20260620-dashboard", + "redis_dash_head": "915d5c2" + }, + "live_image_ids_before_build": { + "pool": "sha256:c102be94afedbf98a51ce9362809198725b290979682e902096f0619a7e74c50", + "dashboard": "sha256:21e9bf98ae9a60e44c379cde8b29a2ddf40fc061dc987aebd3d6db741a18529b", + "node": "sha256:c1b1c24457dc052f0d4352e16cbfbf99144633d5bc3f5f7cf17fb57475933845", + "status_sampler": "sha256:6e45c75e084826a9ec44f0bd101b124610b450f31908396b9972d99685cd7739" + }, + "live_health_before_build": { + "pool_job_state": "ok/ok", + "active_connections": 4, + "authorized_connections": 4, + "ready_connections": 4, + "pool_job_health_ok": 1, + "pool_job_health_ready_miners": 4, + "backend_mineable": 1, + "backend_submit_ready": 1, + "backend_p2p_mining_fresh": 1, + "accepted_block_submit_counter": 1069 + }, + "morning_safe_build_result": { + "built_at": "2026-06-22T08:20:00+02:00", + "deployed": false, + "pool_candidate_tag": "stack-pool:morning-safe-no-a890-20260622-0818", + "pool_candidate_image": "sha256:cd74f1d9d724a30a1ef0f227218195e7ac81fde032f2f0f47aca1ab2cb66ea2d", + "dashboard_candidate_tag": "stack-dashboard:morning-safe-no-a890-20260622-0818", + "dashboard_candidate_image": "sha256:8bbfe43540b1781fe0a0530917dabb2b65bbc4d9c99698f84c357b35ba44f8c8", + "latest_tags_restored_to_live_images": true, + "binary_exclusion_check": "The built pool image contains no awaiting_current_job_broadcast or pool_job_health_current_job_awaiting_broadcast_miners strings.", + "live_state_after_build": "No containers were recreated or restarted. Direct pool health stayed ok/ok with 4 active/authorized/ready ASIC lanes." + }, + "rollback_commands": [ + "docker tag stack-pool:20260622-0735-pre-rebuild-redeploy stack-pool:latest && docker compose up -d --no-deps --no-build pool", + "docker tag stack-dashboard:20260622-0735-pre-rebuild-redeploy stack-dashboard:latest && docker compose up -d --no-deps --no-build dashboard", + "Avoid node rollback for this build; if unavoidable, stop pool first, restore node deliberately, verify native health/fresh peers, then restart pool." + ], + "pre_build_validation": [ + "pool go test ./... -count=1 passed after revert", + "stack p2p module guard passed", + "direct pool API on 127.0.0.1:9090 showed four ready ASIC lanes before build" + ] +} diff --git a/ops/stack_status_source.py b/ops/stack_status_source.py index f8ac3ad..3cd3e8f 100644 --- a/ops/stack_status_source.py +++ b/ops/stack_status_source.py @@ -21,6 +21,7 @@ DEFAULT_STATUS_URL = "http://127.0.0.1:8088/api/status" DEFAULT_TIMEOUT_SECONDS = float(os.environ.get("BDAG_STATUS_SOURCE_TIMEOUT", "20")) +REPAIR_STATUS_REQUIRED_KEYS = ("failures", "warnings", "overall") class StackStatusUnavailable(RuntimeError): @@ -73,6 +74,21 @@ def fetch_http_status(url: str, timeout: float = DEFAULT_TIMEOUT_SECONDS) -> dic return payload +def validate_repair_status_payload(payload: dict[str, Any], source: str) -> None: + """Reject compact dashboard payloads that are not safe for repair actors.""" + missing: list[str] = [] + if not isinstance(payload.get("failures"), list): + missing.append("failures") + if not isinstance(payload.get("warnings"), list): + missing.append("warnings") + if not str(payload.get("overall") or "").strip(): + missing.append("overall") + if missing: + raise StackStatusUnavailable( + f"status payload from {source} is missing repair schema key(s): {', '.join(missing)}" + ) + + def collect_stack_status( *, include_logs: bool = True, @@ -101,7 +117,9 @@ def collect_stack_status( urls = [collector_url] if collector_url else _env_urls() for url in [item for item in urls if item]: try: - return _annotate(fetch_http_status(url, timeout=timeout), "status-http", errors) + payload = fetch_http_status(url, timeout=timeout) + validate_repair_status_payload(payload, url) + return _annotate(payload, "status-http", errors) except (OSError, urllib.error.URLError, TimeoutError, json.JSONDecodeError, StackStatusUnavailable) as exc: errors.append(f"status API {url}: {exc}") diff --git a/ops/status_sampler.py b/ops/status_sampler.py index 3dfc3d0..7cb7a94 100755 --- a/ops/status_sampler.py +++ b/ops/status_sampler.py @@ -42,6 +42,8 @@ write_status_sampler_payload, ) +SAFE_ENV_VALUE_RE = re.compile(r"^[A-Za-z0-9_./:@,%+=-]*$") + def env_float(name: str, default: float, minimum: float | None = None) -> float: try: @@ -249,6 +251,7 @@ def set_env_file_value(path: Any, key: str, value: str) -> bool: env_path = path if hasattr(path, "read_text") else PROJECT_ROOT / str(path) if not env_path.exists(): return False + formatted_value = format_env_value(value) lines = env_path.read_text(encoding="utf-8", errors="replace").splitlines() changed = False found = False @@ -259,13 +262,13 @@ def set_env_file_value(path: Any, key: str, value: str) -> bool: assignment = stripped[7:].strip() if prefix else stripped if assignment.startswith(f"{key}="): found = True - replacement = f"{prefix}{key}={value}" if prefix else f"{key}={value}" + replacement = f"{prefix}{key}={formatted_value}" if prefix else f"{key}={formatted_value}" output.append(replacement) changed = changed or line != replacement else: output.append(line) if not found: - output.append(f"{key}={value}") + output.append(f"{key}={formatted_value}") changed = True if not changed: return False @@ -275,6 +278,18 @@ def set_env_file_value(path: Any, key: str, value: str) -> bool: return True +def format_env_value(value: str) -> str: + if SAFE_ENV_VALUE_RE.match(value): + return value + escaped = ( + value.replace("\\", "\\\\") + .replace('"', '\\"') + .replace("$", "\\$") + .replace("`", "\\`") + ) + return f'"{escaped}"' + + def set_runtime_env_value(key: str, value: str) -> list[str]: changed_paths: list[str] = [] seen: set[Any] = set() @@ -344,11 +359,27 @@ def node_args_assignment_value(args: str, flag: str) -> str | None: return None +def configured_node_obsolete_height() -> str: + value = config_value("BDAG_NODE_OBSOLETE_HEIGHT", "20").strip() + if not value.isdigit(): + return "" + return value + + +def node_mining_no_pending_tx_enabled() -> bool: + return env_enabled_value(config_value("BDAG_NODE_MINING_NO_PENDING_TX", "0"), False) + + def node_mining_runtime_args(address: str) -> str: parts = [ *NODE_MINING_REQUIRED_BOOL_FLAGS, f"--miningaddr={address}", ] + obsolete_height = configured_node_obsolete_height() + if obsolete_height: + parts.append(f"--obsoleteheight={obsolete_height}") + if node_mining_no_pending_tx_enabled(): + parts.append("--miningnopendingtx") if constrained_storage_profile(): # A USB-backed ASIC router should mine and relay blocks, not serve as a # catch-up source for other peers while it is trying to convert shares @@ -367,6 +398,14 @@ def node_mining_args_are_safe_and_complete(args: str, address: str) -> bool: for flag in NODE_MINING_REQUIRED_BOOL_FLAGS: if not node_args_have_bool_flag(args, flag): return False + obsolete_height = configured_node_obsolete_height() + if obsolete_height and node_args_assignment_value(args, "--obsoleteheight") != obsolete_height: + return False + if node_mining_no_pending_tx_enabled(): + if not node_args_have_bool_flag(args, "--miningnopendingtx"): + return False + elif node_args_have_bool_flag(args, "--miningnopendingtx"): + return False if constrained_storage_profile(): for flag, wanted in NODE_MINING_CONSTRAINED_ASSIGNMENTS.items(): if node_args_assignment_value(args, flag) != wanted: @@ -508,22 +547,42 @@ def public_chain_divergence_reasons(payload: dict[str, Any]) -> list[str]: return sorted(set(str(item) for item in reasons if item)) +def native_chain_progress_advisory_safe(payload: dict[str, Any]) -> tuple[bool, str]: + sync_health = dict_value(payload.get("sync_health")) + for key, reason in ( + ("native_chain_progress_safe", "native chain progress is already marked safe"), + ("selected_backend_native_p2p_current_safe", "selected backend native P2P/current-chain proof is marked safe"), + ("selected_backend_mining_safe", "selected backend is marked mining-safe"), + ("native_progress_paid_work_safe", "native progress and recent paid work are already marked safe"), + ): + if sync_health.get(key) is True: + return True, reason + native_safe, native_reason = pool_start_gate.native_mining_safety_proven(payload) + if native_safe: + return True, native_reason + return False, native_reason + + def catchup_lag_blocks(payload: dict[str, Any]) -> int: values: list[int] = [] + paid_work_recent = pool_has_recent_paid_work(payload) + native_advisory_safe, _native_advisory_reason = native_chain_progress_advisory_safe(payload) + remaining_blocks_advisory = bool(paid_work_recent or native_advisory_safe) + lag_keys = ("peer_ahead_blocks",) if remaining_blocks_advisory else ("remaining_blocks", "peer_ahead_blocks") policy = dict_value(payload.get("catchup_policy")) policy_lag = safe_int(policy.get("lag_blocks"), -1) - if policy_lag >= 0: + if policy_lag >= 0 and not remaining_blocks_advisory: values.append(policy_lag) sync = dict_value(payload.get("sync_progress")) - for key in ("remaining_blocks", "peer_ahead_blocks"): + for key in lag_keys: value = safe_int(sync.get(key), -1) if value >= 0: values.append(value) for info in dict_value(sync.get("nodes")).values(): if not isinstance(info, dict): continue - for key in ("remaining_blocks", "peer_ahead_blocks"): + for key in lag_keys: value = safe_int(info.get(key), -1) if value >= 0: values.append(value) @@ -544,6 +603,64 @@ def catchup_lag_blocks(payload: dict[str, Any]) -> int: return max(values) if values else 0 +def native_peer_lag_blocks(payload: dict[str, Any]) -> int: + values: list[int] = [] + + def add(value: Any) -> None: + lag = safe_int(value, -1) + if lag >= 0: + values.append(lag) + + sync = dict_value(payload.get("sync_progress")) + add(sync.get("peer_ahead_blocks")) + for info in dict_value(sync.get("nodes")).values(): + if isinstance(info, dict): + add(info.get("peer_ahead_blocks")) + for info in dict_value(payload.get("nodes")).values(): + if isinstance(info, dict): + add(info.get("peer_ahead_blocks")) + + selected_health = dict_value( + dict_value(payload.get("pool_metrics")).get("selected_backend_source_health") + ) or dict_value(dict_value(payload.get("pool")).get("selected_backend_source_health")) + add(selected_health.get("node_p2p_best_peer_lead_blocks")) + return max(values) if values else 0 + + +def has_active_native_p2p_lag_evidence(payload: dict[str, Any]) -> bool: + def progress_evidence(progress: dict[str, Any]) -> bool: + status_text = str(progress.get("status") or "").strip().lower() + error_text = str(progress.get("error") or "").strip().lower() + if status_text == "p2p_down": + return False + if "no active native p2p peers" in error_text or "syncing but no active peers" in error_text: + return False + if safe_int(progress.get("peer_count"), -1) > 0 or safe_int(progress.get("p2p_connections"), -1) > 0: + return True + health = dict_value(progress.get("native_template_health")) + if safe_int(health.get("p2p_fresh_consensus_peer_count"), -1) > 0: + return True + if safe_int(health.get("p2p_consensus_peer_count"), -1) > 0: + return True + return health.get("p2p_mining_fresh") is True + + sync = dict_value(payload.get("sync_progress")) + if progress_evidence(sync): + return True + for info in dict_value(sync.get("nodes")).values(): + if isinstance(info, dict) and progress_evidence(info): + return True + + selected_health = dict_value( + dict_value(payload.get("pool_metrics")).get("selected_backend_source_health") + ) or dict_value(dict_value(payload.get("pool")).get("selected_backend_source_health")) + if selected_health.get("node_p2p_mining_fresh") is True: + return True + if safe_int(selected_health.get("node_p2p_fresh_consensus_peer_count"), -1) > 0: + return True + return safe_int(selected_health.get("node_p2p_consensus_peer_count"), -1) > 0 + + def catchup_io_pressure_reasons(payload: dict[str, Any]) -> list[str]: host_pressure = dict_value(payload.get("host_pressure")) reasons: list[str] = [] @@ -567,12 +684,22 @@ def catchup_policy_from_payload(payload: dict[str, Any]) -> dict[str, Any]: lag = catchup_lag_blocks(payload) sync = dict_value(payload.get("sync_progress")) sync_status = str(sync.get("status") or "").strip().lower() + paid_work_recent = pool_has_recent_paid_work(payload) + native_advisory_safe, native_advisory_reason = native_chain_progress_advisory_safe(payload) + remaining_blocks_advisory = bool(paid_work_recent or native_advisory_safe) + mining_ready = bool( + policy.get("mining_ready", payload.get("can_mine") is True) + or paid_work_recent + or native_advisory_safe + ) + cached_syncing_active = bool(policy.get("syncing_active")) and not remaining_blocks_advisory syncing_active = bool( - policy.get("syncing_active") + cached_syncing_active or ( CATCHUP_PAUSE_ON_SYNCING and sync_status in {"syncing", "catchup_pause"} and lag > 0 + and not mining_ready ) ) io_pressure_reasons = policy.get("io_pressure_reasons") @@ -580,7 +707,6 @@ def catchup_policy_from_payload(payload: dict[str, Any]) -> dict[str, Any]: io_pressure_reasons = catchup_io_pressure_reasons(payload) io_pressure_enabled = bool(policy.get("io_pressure_pause_enabled", CATCHUP_IO_PRESSURE_PAUSE_ENABLED)) io_min_lag = safe_int(policy.get("io_pressure_min_lag_blocks"), CATCHUP_IO_PRESSURE_MIN_LAG_BLOCKS) - mining_ready = bool(policy.get("mining_ready", payload.get("can_mine") is True)) backend_unready_under_pressure = bool( policy.get("backend_unready_under_pressure") or (io_pressure_reasons and not mining_ready and payload.get("can_mine") is False) @@ -591,10 +717,16 @@ def catchup_policy_from_payload(payload: dict[str, Any]) -> dict[str, Any]: and not mining_ready and (lag >= io_min_lag or backend_unready_under_pressure) ) - lag_threshold_active = bool(lag > threshold and (not chain_ready_for_mining(payload) or not mining_ready)) + lag_threshold_active = bool( + lag > threshold + and not mining_ready + and not chain_ready_for_mining(payload) + ) active = bool(policy.get("active")) if "active" in policy else False if not active: active = bool(CATCHUP_PAUSE_ENABLED and (syncing_active or io_pressure_active or lag_threshold_active)) + if active and remaining_blocks_advisory and not (syncing_active or io_pressure_active or lag_threshold_active): + active = False trigger = str(policy.get("trigger") or "") if not trigger and active: if syncing_active: @@ -622,6 +754,9 @@ def catchup_policy_from_payload(payload: dict[str, Any]) -> dict[str, Any]: "backend_unready_under_pressure": backend_unready_under_pressure, "lag_threshold_active": lag_threshold_active, "mining_ready": mining_ready, + "remaining_blocks_advisory": remaining_blocks_advisory, + "native_advisory_safe": native_advisory_safe, + "native_advisory_reason": native_advisory_reason if native_advisory_safe else "", } @@ -632,7 +767,12 @@ def catchup_pause_active(payload: dict[str, Any]) -> bool: def chain_state_restore_hard_reasons(payload: dict[str, Any]) -> list[str]: reasons: list[str] = [] sync_health = dict_value(payload.get("sync_health")) - if sync_health.get("needs_chain_data_restore") or sync_health.get("chain_data_restore_required"): + soft_evm_restore_only = evm_reference_gap_restore_only(sync_health) + evm_restore_advisory, _advisory_reason = evm_reference_gap_native_paid_advisory(payload) + if ( + sync_health.get("needs_chain_data_restore") + or sync_health.get("chain_data_restore_required") + ) and not (soft_evm_restore_only and evm_restore_advisory): restore_nodes = dict_value(sync_health.get("chain_data_restore_nodes")) for node, info in restore_nodes.items(): node_reasons = info.get("reasons") if isinstance(info, dict) else None @@ -666,6 +806,32 @@ def chain_state_restore_hard_reasons(payload: dict[str, Any]) -> list[str]: return sorted(set(reasons)) +def evm_reference_gap_restore_only(sync_health: dict[str, Any]) -> bool: + if not ( + sync_health.get("evm_reference_gap_stalled") + or dict_value(sync_health.get("evm_reference_gap_watch")).get("restore_required") + ): + return False + return not bool( + sync_health.get("chain_state_blocker") + or sync_health.get("chain_data_restore_required") + or dict_value(sync_health.get("chain_state_blocker_nodes")) + or dict_value(sync_health.get("chain_data_restore_nodes")) + ) + + +def evm_reference_gap_native_paid_advisory(payload: dict[str, Any]) -> tuple[bool, str]: + sync_health = dict_value(payload.get("sync_health")) + if sync_health.get("native_progress_paid_work_safe") is True: + return True, "native progress and recent paid work are already marked safe" + if sync_health.get("selected_backend_mining_safe") is True: + reason = "selected backend is mining-safe" + if sync_health.get("pool_has_recent_paid_work") is True: + reason += " and recent paid work is present" + return True, reason + return native_chain_progress_advisory_safe(payload) + + def sync_progress_height(payload: dict[str, Any]) -> int: sync = dict_value(payload.get("sync_progress")) values: list[int] = [] @@ -704,35 +870,45 @@ def update_stalled_import_watch(payload: dict[str, Any]) -> dict[str, Any]: sync = dict_value(payload.get("sync_progress")) status = str(sync.get("status") or payload.get("mode") or "").lower() height = sync_progress_height(payload) - lag = catchup_lag_blocks(payload) + lag = native_peer_lag_blocks(payload) + native_p2p_evidence = has_active_native_p2p_lag_evidence(payload) candidate = bool( CHAIN_STATE_STALLED_IMPORT_RESTORE_ENABLED and status in {"syncing", "catchup_pause"} and height >= 0 + and native_p2p_evidence and lag >= CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS ) previous = read_import_watch_state() previous_height = safe_int(previous.get("height"), -1) - if not candidate or previous_height != height: + previous_first_stalled_epoch = safe_float(previous.get("first_stalled_epoch"), 0.0) + if not candidate or previous_height != height or previous_first_stalled_epoch <= 0: + reason = "height changed or stall candidate inactive" + if not native_p2p_evidence: + reason = "stall candidate inactive: no active native P2P peer-lag evidence" + elif candidate and previous_first_stalled_epoch <= 0: + reason = "stall candidate started or invalid previous stall epoch reset" state = { "schema_version": 1, "updated_at": now_iso(), "epoch": now_epoch, + "candidate": candidate, "status": status, "height": height, "lag_blocks": lag, + "native_p2p_lag_evidence": native_p2p_evidence, "first_stalled_epoch": now_epoch if candidate else 0, "stalled_seconds": 0, "min_lag_blocks": lag if candidate else 0, "max_lag_blocks": lag if candidate else 0, "gap_growth_blocks": 0, "restore_required": False, - "reason": "height changed or stall candidate inactive", + "reason": reason, } write_json_file(CHAIN_STATE_IMPORT_WATCH_FILE, state, mode=0o600) return state - first_stalled_epoch = safe_float(previous.get("first_stalled_epoch"), now_epoch) + first_stalled_epoch = previous_first_stalled_epoch min_lag = min(safe_int(previous.get("min_lag_blocks"), lag), lag) max_lag = max(safe_int(previous.get("max_lag_blocks"), lag), lag) stalled_seconds = max(0, int(now_epoch - first_stalled_epoch)) @@ -749,9 +925,11 @@ def update_stalled_import_watch(payload: dict[str, Any]) -> dict[str, Any]: "schema_version": 1, "updated_at": now_iso(), "epoch": now_epoch, + "candidate": True, "status": status, "height": height, "lag_blocks": lag, + "native_p2p_lag_evidence": native_p2p_evidence, "first_stalled_epoch": first_stalled_epoch, "stalled_seconds": stalled_seconds, "min_lag_blocks": min_lag, @@ -894,12 +1072,23 @@ def update_evm_reference_gap_watch(payload: dict[str, Any]) -> dict[str, Any]: f"for {stalled_seconds}s while lag is {lag} block(s)" ) + would_restore_required = restore_required + advisory_safe, advisory_reason = evm_reference_gap_native_paid_advisory(payload) + if restore_required and advisory_safe: + restore_required = False + reason = f"{reason}; advisory under native mining safety: {advisory_reason}" + state = { "schema_version": 1, "updated_at": now_iso(), "epoch": now_epoch, "candidate": True, "restore_required": restore_required, + "would_restore_required": would_restore_required, + "restore_suppressed_by_native_mining_safety": bool(would_restore_required and advisory_safe), + "restore_suppressed_by_native_paid_work": bool(would_restore_required and advisory_safe), + "native_paid_work_advisory_reason": advisory_reason if advisory_safe else "", + "native_mining_safety_advisory_reason": advisory_reason if advisory_safe else "", "reason": reason, "sample": sample, "lag_blocks": lag, @@ -1035,6 +1224,8 @@ def status_payload_has_tracking_gap(payload: dict[str, Any]) -> bool: miner_health = dict_value(payload.get("miner_health")) if safe_int(miner_health.get("tracked_count")) > 0: return False + if "tracked_count" not in miner_health and miner_health.get("connected_count_source") == "pool-metrics": + return False return status_payload_has_miner_demand(payload) or asic_lan_neighbor_present() @@ -1070,6 +1261,55 @@ def miner_row_has_visible_share_evidence(row: dict[str, Any]) -> bool: return False +def pool_has_recent_paid_work(payload: dict[str, Any]) -> bool: + sync_health = dict_value(payload.get("sync_health")) + if sync_health.get("pool_has_recent_paid_work") is True: + return True + paid_work = dict_value(sync_health.get("pool_paid_work_state")) + if paid_work.get("accepted_block_recent") is True: + return True + pool = dict_value(payload.get("pool")) + pool_health = dict_value(payload.get("pool_health")) + for source in (pool, pool_health): + accepted = safe_int(source.get("block_submit_success_count")) + age = safe_float(source.get("last_block_submit_age_seconds"), default=10_000) + if accepted > 0 and age <= MINING_IMPERATIVE_MINER_ACTIVITY_STALE_SECONDS: + return True + return False + + +def pool_has_any_paid_work_evidence(payload: dict[str, Any]) -> bool: + sync_health = dict_value(payload.get("sync_health")) + paid_work = dict_value(sync_health.get("pool_paid_work_state")) + sources = ( + paid_work, + dict_value(payload.get("pool")), + dict_value(payload.get("pool_health")), + dict_value(payload.get("pool_metrics")), + ) + for source in sources: + for key in ( + "accepted_block_submissions", + "accepted_block_submission_count", + "block_submit_success_count", + "accepted_blocks", + "blocks_found", + ): + if safe_int(source.get(key)) > 0: + return True + return False + + +def live_pool_state_blocks_node_runtime_mutation(payload: dict[str, Any]) -> bool: + if pool_container_running(payload): + return True + if pool_has_recent_paid_work(payload): + return True + if pool_has_any_paid_work_evidence(payload): + return True + return False + + def status_payload_has_miner_activity_visibility_gap(payload: dict[str, Any]) -> bool: if not MINING_IMPERATIVE_MINER_ACTIVITY_REPAIR_ENABLED: return False @@ -1162,19 +1402,29 @@ def node_mining_template_support_should_repair(payload: dict[str, Any]) -> bool: address = configured_mining_address() if not valid_mining_address(address): return False + if not pool_start_gate.pool_start_decision(payload).allowed: + return False + if live_pool_state_blocks_node_runtime_mutation(payload): + return False + args = config_value("BDAG_NODE_MINING_ARGS") + append_args = config_value("NODE_ARGS_APPEND") + unsafe_args_present = any( + flag in f"{args} {append_args}" + for flag in ("--allowminingwhennearlysynced", "--allowsubmitwhennotsynced") + ) + if pool_has_recent_paid_work(payload) and not unsafe_args_present: + return False modules = { item.strip().lower() for item in config_value("BDAG_NODE_MODULES", NODE_MINING_MODULES).split(",") if item.strip() } - args = config_value("BDAG_NODE_MINING_ARGS") if not env_enabled_value(config_value("BDAG_ENABLE_NODE_MINING"), False): return True if modules != NODE_MINING_MODULE_SET: return True if not node_mining_args_are_safe_and_complete(args, address): return True - append_args = config_value("NODE_ARGS_APPEND") if append_args and not node_mining_args_are_safe_and_complete(append_args, address): return True for service in node_services_for_recreate(): @@ -1256,6 +1506,25 @@ def automation_repair_mutation_allowed( def recreate_node_services(payload: dict[str, Any], reason: str) -> tuple[bool, list[dict[str, Any]]]: + if live_pool_state_blocks_node_runtime_mutation(payload): + node_results = [ + { + "service": service, + "returncode": None, + "ok": False, + "blocked": True, + "blocked_reason": "live pool or paid-block evidence blocks node recreate", + } + for service in node_services_for_recreate() + ] + record_incident( + "mining_imperative_node_recreate_live_pool_blocked", + "critical", + "Mining imperative left node services unchanged because live pool or paid-block evidence blocks recreate", + {"reason": reason, "node_recreate_results": node_results}, + payload, + ) + return False, node_results node_results = [] ok = True for service in node_services_for_recreate(): @@ -1384,6 +1653,16 @@ def repair_miner_activity_visibility(payload: dict[str, Any]) -> bool: def repair_fastsync_orphan_peers(payload: dict[str, Any]) -> bool: peer_ids = fastsync_orphan_peer_ids(payload) + if live_pool_state_blocks_node_runtime_mutation(payload): + log("mining imperative left FastSync peer config unchanged because live pool state blocks node mutation") + record_incident( + "mining_imperative_fastsync_peer_quarantine_live_pool_blocked", + "critical", + "FastSync orphan peer quarantine was deferred because live pool or paid-block evidence blocks node mutation", + {"peer_ids": peer_ids}, + payload, + ) + return False if not automation_repair_mutation_allowed( automation_control.ACTION_CONFIG_EDIT, target="fastsync-peer-config", @@ -1515,6 +1794,19 @@ def catchup_target_node_cache_mb() -> int: def apply_catchup_node_runtime(payload: dict[str, Any], policy: dict[str, Any]) -> bool: + if pool_has_recent_paid_work(payload): + log("catch-up runtime adjustment skipped because accepted block submissions remain recent") + return False + native_advisory_safe, native_advisory_reason = native_chain_progress_advisory_safe(payload) + if policy.get("native_advisory_safe") is True or native_advisory_safe: + log( + "catch-up runtime adjustment skipped because native mining safety makes " + f"EVM/public lag advisory: {policy.get('native_advisory_reason') or native_advisory_reason}" + ) + return False + if live_pool_state_blocks_node_runtime_mutation(payload): + log("catch-up runtime adjustment skipped because live pool/miner state blocks node mutation") + return False if not automation_repair_mutation_allowed( automation_control.ACTION_CONFIG_EDIT, target="catchup-node-runtime", diff --git a/ops/sync_coordinator.py b/ops/sync_coordinator.py index f1ad7b3..946dcca 100644 --- a/ops/sync_coordinator.py +++ b/ops/sync_coordinator.py @@ -64,7 +64,11 @@ def build_state() -> dict[str, Any]: if isinstance(sync_health.get("evm_reference_gap_watch"), dict) else {} ) - evm_gap_restore_required = bool(evm_gap_watch.get("restore_required")) + native_paid_safe = bool( + sync_health.get("native_progress_paid_work_safe") + or (sync_health.get("selected_backend_mining_safe") and sync_health.get("pool_has_recent_paid_work")) + ) + evm_gap_restore_required = bool(evm_gap_watch.get("restore_required") and not native_paid_safe) active_node = NODES[0] if NODES else "node" node_info = nodes.get(active_node, {}) if isinstance(nodes, dict) else {} remaining = safe_int(node_info.get("remaining_blocks"), safe_int(sync_progress.get("remaining_blocks") if isinstance(sync_progress, dict) else 0)) diff --git a/ops/systemd/bdag-p2p-firewall.service b/ops/systemd/bdag-p2p-firewall.service index d5f6995..eb77022 100644 --- a/ops/systemd/bdag-p2p-firewall.service +++ b/ops/systemd/bdag-p2p-firewall.service @@ -5,7 +5,7 @@ Wants=network-online.target docker.service [Service] Type=oneshot -Environment=P2P_PORT=8150 +Environment=P2P_PORT=8155 Environment=BDAG_P2P_PROTOCOLS=tcp EnvironmentFile=-/etc/default/bdag-p2p-firewall ExecStart=/usr/local/sbin/bdag-allow-p2p-iptables diff --git a/ops/tests/test_automation_control.py b/ops/tests/test_automation_control.py index f35fefa..e555d64 100644 --- a/ops/tests/test_automation_control.py +++ b/ops/tests/test_automation_control.py @@ -131,6 +131,46 @@ def test_repair_hold_controlled_stop_and_chain_incident_deny_high_risk_mutations self.assertIn("denies high-risk", decision.reason) self.assertEqual(1, len(self.event_lines())) + def test_repair_hold_allows_only_allowlisted_mining_recovery_actions(self) -> None: + self.write_state( + self.control_state( + "repair_hold", + allowed_mutations=[ + f"{automation_control.ACTION_ASIC_POOL_START}:asic-pool", + f"{automation_control.ACTION_ASIC_POOL_RESTART}:asic-pool", + f"{automation_control.ACTION_ASIC_MINER_OPEN_RESTART}:*", + f"{automation_control.ACTION_NODE_RESTART}:*", + f"{automation_control.ACTION_CONFIG_EDIT}:*", + ], + ) + ) + + pool_start = self.check(automation_control.ACTION_ASIC_POOL_START, target="asic-pool") + pool_restart = self.check(automation_control.ACTION_ASIC_POOL_RESTART, target="asic-pool") + miner_restart = self.check(automation_control.ACTION_ASIC_MINER_OPEN_RESTART, target="192.168.1.101") + node_restart = self.check(automation_control.ACTION_NODE_RESTART, target="node") + config_edit = self.check(automation_control.ACTION_CONFIG_EDIT, target="node.env") + + self.assertTrue(pool_start.allowed, pool_start.reason) + self.assertTrue(pool_restart.allowed, pool_restart.reason) + self.assertTrue(miner_restart.allowed, miner_restart.reason) + self.assertFalse(node_restart.allowed) + self.assertFalse(config_edit.allowed) + self.assertEqual(2, len(self.event_lines())) + + def test_controlled_stop_ignores_mining_recovery_allowlist(self) -> None: + self.write_state( + self.control_state( + "controlled_stop", + allowed_mutations=[f"{automation_control.ACTION_ASIC_POOL_START}:asic-pool"], + ) + ) + + decision = self.check(automation_control.ACTION_ASIC_POOL_START, target="asic-pool") + + self.assertFalse(decision.allowed) + self.assertIn("controlled_stop", decision.reason) + def test_normal_control_allows_high_risk_mutation(self) -> None: self.write_state(self.control_state("normal")) @@ -384,6 +424,127 @@ def test_watchdog_api_stall_miner_restart_uses_open_restart_before_configure(sel self.assertEqual("running", writes[0]["status"]) self.assertEqual("ok", writes[-1]["status"]) + def test_watchdog_flags_missing_managed_asic_lane_when_api_stalls(self) -> None: + worker = "0x1719E0ee598c15957448D5E568948101DF78e7A0" + expected_url = pool_ops.default_miner_pool_settings()["pool_url"] + managed_rows = [ + { + "ip": "192.168.1.101", + "mac": "28:e2:97:1e:c0:b5", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + { + "ip": "192.168.1.102", + "mac": "2a:71:c7:f5:1f:1e", + "device_id": "mac:2a:71:c7:f5:1f:1e", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + { + "ip": "192.168.1.105", + "mac": "28:e2:97:4d:44:3a", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + { + "ip": "192.168.1.14", + "mac": "28:e2:97:3e:39:63", + "device_id": "mac:28:e2:97:3e:39:63", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + ] + status = { + "mining_address": worker, + "pool_health": {"job_notify_count": 100}, + "pool_metrics": {"active_connections": 3, "authorized_miners": 3, "ready_miners": 3}, + "miner_health": {"miners": managed_rows}, + } + active_lanes = [ + {"mac": "28:e2:97:1e:c0:b5", "pool_job_state_authorized": True}, + {"mac": "2a:71:c7:f5:1f:1e", "pool_job_state_authorized": True}, + {"mac": "28:e2:97:3e:39:63", "pool_job_state_authorized": True}, + ] + + with unittest.mock.patch.object(watchdog, "collect_pool_job_state_activity", return_value=active_lanes), unittest.mock.patch.object( + watchdog, "get_miner_pools", side_effect=TimeoutError("timed out") + ), unittest.mock.patch.object( + watchdog, "get_miner_cgminer_devs", side_effect=TimeoutError("timed out") + ): + affected = watchdog.asic_api_stall_primary_miners(status) + + self.assertEqual(["192.168.1.105"], [item["ip"] for item in affected]) + self.assertTrue(affected[0]["pool_job_state_missing_lane"]) + self.assertTrue(affected[0]["restart_open_first"]) + self.assertEqual(watchdog.DEFAULT_ASIC_MISSING_LANE_CONFIRM_SECONDS, affected[0]["api_stall_confirm_seconds"]) + + def test_watchdog_does_not_flag_missing_asic_lane_when_api_is_healthy(self) -> None: + worker = "0x1719E0ee598c15957448D5E568948101DF78e7A0" + expected_url = pool_ops.default_miner_pool_settings()["pool_url"] + status = { + "mining_address": worker, + "pool_health": {"job_notify_count": 100}, + "pool_metrics": {"active_connections": 1, "authorized_miners": 1, "ready_miners": 1}, + "miner_health": { + "miners": [ + { + "ip": "192.168.1.101", + "mac": "28:e2:97:1e:c0:b5", + "device_id": "mac:28:e2:97:1e:c0:b5", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + { + "ip": "192.168.1.105", + "mac": "28:e2:97:4d:44:3a", + "device_id": "mac:28:e2:97:4d:44:3a", + "device_type": "asic", + "managed": True, + "connected": False, + "status": "configured", + "expected_pool_url": expected_url, + "expected_worker_user": worker, + }, + ] + }, + } + + with unittest.mock.patch.object( + watchdog, + "collect_pool_job_state_activity", + return_value=[{"mac": "28:e2:97:1e:c0:b5", "pool_job_state_authorized": True}], + ), unittest.mock.patch.object( + watchdog, "get_miner_pools", return_value=[{"active": True}] + ), unittest.mock.patch.object( + watchdog, "get_miner_cgminer_devs", return_value={"minerstatus": "Mining"} + ): + affected = watchdog.asic_api_stall_primary_miners(status) + + self.assertEqual([], affected) + def test_sentinel_suppresses_pool_starts_when_control_missing(self) -> None: incidents: list[tuple[str, str, str, str]] = [] diff --git a/ops/tests/test_chain_rpc_resilience.py b/ops/tests/test_chain_rpc_resilience.py index 9ba0751..a2a2d98 100644 --- a/ops/tests/test_chain_rpc_resilience.py +++ b/ops/tests/test_chain_rpc_resilience.py @@ -17,6 +17,8 @@ def setUp(self) -> None: self.old_pool_rpc_refused_warn_seconds = pool_ops.POOL_RPC_REFUSED_WARN_SECONDS self.old_mining_rpc_call = pool_ops.mining_rpc_call self.old_json_rpc_call = pool_ops.json_rpc_call + self.old_node_rpc_urls = pool_ops.node_rpc_urls + self.old_node_sync_progress = pool_ops.node_sync_progress self.old_evm_reference_rpc_urls = pool_ops.evm_reference_rpc_urls self.old_alignment_always_sample = pool_ops.EVM_PUBLIC_ALIGNMENT_ALWAYS_SAMPLE self.old_alignment_min_samples = pool_ops.EVM_PUBLIC_ALIGNMENT_MIN_SAMPLES @@ -31,6 +33,8 @@ def restore_globals(self) -> None: pool_ops.POOL_RPC_REFUSED_WARN_SECONDS = self.old_pool_rpc_refused_warn_seconds pool_ops.mining_rpc_call = self.old_mining_rpc_call pool_ops.json_rpc_call = self.old_json_rpc_call + pool_ops.node_rpc_urls = self.old_node_rpc_urls + pool_ops.node_sync_progress = self.old_node_sync_progress pool_ops.evm_reference_rpc_urls = self.old_evm_reference_rpc_urls pool_ops.EVM_PUBLIC_ALIGNMENT_ALWAYS_SAMPLE = self.old_alignment_always_sample pool_ops.EVM_PUBLIC_ALIGNMENT_MIN_SAMPLES = self.old_alignment_min_samples @@ -215,7 +219,7 @@ def fake_json_rpc(url, method, params, timeout): self.assertEqual(progress["canonical_mining_safety"]["hash_mismatch_count"], 2) self.assertFalse(progress["canonical_mining_safety"]["public_chain_diverged"]) - def test_eth_syncing_overrides_native_template_synced_status(self) -> None: + def test_native_template_health_overrides_eth_syncing_for_mining_status(self) -> None: pool_ops.NODE_CHAIN_RPC_RETRIES = 1 def fake_mining_rpc(_url, method, _params, timeout): @@ -257,6 +261,109 @@ def fake_json_rpc(_url, method, _params, timeout): progress = pool_ops.node_sync_progress("node", "http://local:38131", timeout=8.0) + self.assertEqual(progress["status"], "synced") + self.assertEqual(progress["source"], "node:native-template-health") + self.assertEqual(progress["current_block"], 12000) + self.assertEqual(progress["highest_block"], 12000) + self.assertEqual(progress["remaining_blocks"], 0) + self.assertEqual(progress["current_block_source"], "getBlockCount") + self.assertFalse(progress["chain_syncing"]) + self.assertTrue(progress["evm_chain_syncing"]) + self.assertTrue(progress["mining_advisory_sync"]) + self.assertTrue(progress["native_template_health"]["mineable_now"]) + + def test_collect_sync_progress_keeps_evm_sync_advisory_out_of_native_chain_sync(self) -> None: + pool_ops.node_rpc_urls = lambda: [("node", "http://local:38131")] + pool_ops.node_sync_progress = lambda _source, _url: { + "status": "synced", + "percent": 100.0, + "current_block": 12000, + "highest_block": 12000, + "remaining_blocks": 0, + "source": "node:native-template-health", + "chain_rpc_source": "getBlockCount", + "chain_block_count": 12000, + "native_is_current": True, + "chain_syncing": False, + "evm_chain_syncing": True, + "mining_advisory_sync": True, + "p2p_network_gap": 0, + "peer_mainorder_gap": 0, + } + + progress = pool_ops.collect_sync_progress() + + self.assertEqual(progress["status"], "synced") + self.assertTrue(progress["native_is_current"]) + self.assertFalse(progress["chain_syncing"]) + self.assertTrue(progress["evm_chain_syncing"]) + self.assertTrue(progress["mining_advisory_sync"]) + self.assertEqual(progress["remaining_blocks"], 0) + + def test_native_current_progress_counts_as_synced_for_busy_log_suppression(self) -> None: + progress = { + "status": "synced", + "native_is_current": True, + "chain_syncing": False, + "evm_chain_syncing": True, + "mining_advisory_sync": True, + "remaining_blocks": 0, + "p2p_network_gap": 0, + "peer_mainorder_gap": 0, + } + + self.assertTrue(pool_ops.sync_progress_native_template_health_synced(progress)) + + progress["chain_syncing"] = True + progress["evm_chain_syncing"] = False + progress["mining_advisory_sync"] = False + + self.assertFalse(pool_ops.sync_progress_native_template_health_synced(progress)) + + def test_eth_syncing_blocks_status_when_native_template_health_is_unsafe(self) -> None: + pool_ops.NODE_CHAIN_RPC_RETRIES = 1 + + def fake_mining_rpc(_url, method, _params, timeout): + if method == "getBlockCount": + return "12000" + if method == "getMainChainHeight": + return "9000" + if method == "getTemplateHealth": + return { + "reason_code": "node_syncing", + "mineable_now": False, + "submit_ready": False, + "get_block_template_ready": False, + "get_block_template_reason_code": "node_syncing", + "p2p_current": True, + "p2p_mining_fresh": True, + "sync_allowed": True, + "chain_current": True, + "main_order": 12000, + "p2p_best_peer_main_order": 12000, + "p2p_best_peer_lead_blocks": 0, + "p2p_fresh_consensus_peer_count": 3, + "p2p_consensus_peer_count": 3, + } + raise AssertionError(method) + + def fake_json_rpc(_url, method, _params, timeout): + if method == "eth_syncing": + return { + "currentBlock": "0x1f40", + "highestBlock": "0x2710", + "startingBlock": "0x0", + } + if method == "eth_blockNumber": + return "0x1f40" + raise AssertionError(method) + + pool_ops.mining_rpc_call = fake_mining_rpc + pool_ops.json_rpc_call = fake_json_rpc + pool_ops.evm_reference_rpc_urls = lambda: [] + + progress = pool_ops.node_sync_progress("node", "http://local:38131", timeout=8.0) + self.assertEqual(progress["status"], "syncing") self.assertEqual(progress["source"], "node:eth_syncing") self.assertEqual(progress["current_block"], 8000) @@ -264,7 +371,7 @@ def fake_json_rpc(_url, method, _params, timeout): self.assertEqual(progress["remaining_blocks"], 2000) self.assertEqual(progress["current_block_source"], "eth_syncing") self.assertTrue(progress["chain_syncing"]) - self.assertTrue(progress["native_template_health"]["mineable_now"]) + self.assertFalse(progress["native_template_health"]["mineable_now"]) def test_native_template_health_blocks_synced_status_when_peer_lead_is_unsafe(self) -> None: pool_ops.NODE_CHAIN_RPC_RETRIES = 1 diff --git a/ops/tests/test_chain_state_self_heal_mainnet_only.py b/ops/tests/test_chain_state_self_heal_mainnet_only.py index 12f1927..1b5e909 100644 --- a/ops/tests/test_chain_state_self_heal_mainnet_only.py +++ b/ops/tests/test_chain_state_self_heal_mainnet_only.py @@ -7,12 +7,16 @@ class ChainStateSelfHealMainnetOnlyTest(unittest.TestCase): def test_self_heal_refuses_non_mainnet_network_and_pins_restore_path(self) -> None: - script = (ROOT / "ops" / "chain-state-self-heal.sh").read_text(encoding="utf-8") + script_path = ROOT / "ops" / "chain-state-self-heal.sh" + script = script_path.read_text(encoding="utf-8") self.assertIn("chain-state self-heal refuses non-mainnet network", script) self.assertIn('NETWORK="mainnet"', script) self.assertIn('NODE_NETWORK_DIR="$NODE_DATA_DIR/$NETWORK"', script) + self.assertIn("soft_evm_restore_only", script) + self.assertIn("native_paid_safe", script) self.assertNotIn('${NETWORK:-mainnet}', script) + self.assertTrue(script_path.stat().st_mode & 0o111) if __name__ == "__main__": diff --git a/ops/tests/test_compose_migrations.py b/ops/tests/test_compose_migrations.py index 52d9fa2..5442816 100644 --- a/ops/tests/test_compose_migrations.py +++ b/ops/tests/test_compose_migrations.py @@ -15,6 +15,7 @@ def test_release_env_defaults_keep_sync_source_and_reconnect_guards(self) -> Non self.assertIn("SYNC_SOURCE_NODE=0\n", env_example) self.assertIn("POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=3\n", env_example) + self.assertIn("POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD=0\n", env_example) self.assertIn("POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=120\n", env_example) self.assertIn("POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=60\n", env_example) @@ -36,11 +37,12 @@ def test_adds_submit_hardening_flags_to_each_existing_pool_service(self) -> None result = compose_migrations.ensure_pool_submit_hardening_flags(compose) self.assertTrue(result.changed) - self.assertEqual(12, result.inserted_count) + self.assertEqual(14, result.inserted_count) self.assertIn( " POOL_SUBMIT_STALE_BLOCK_CANDIDATES: ${POOL_SUBMIT_STALE_BLOCK_CANDIDATES:-false}\n" " POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: ${POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED:-true}\n" " POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD:-1}\n" + " POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD:-3}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS:-120}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS:-60}\n" @@ -54,6 +56,7 @@ def test_adds_submit_hardening_flags_to_each_existing_pool_service(self) -> None " POOL_SUBMIT_STALE_BLOCK_CANDIDATES: ${POOL_SUBMIT_STALE_BLOCK_CANDIDATES:-false}\n" " POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: ${POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED:-true}\n" " POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD:-1}\n" + " POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD:-3}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS:-120}\n" " POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS:-60}\n" @@ -75,6 +78,7 @@ def test_existing_submit_hardening_flags_are_noop(self) -> None: POOL_SUBMIT_STALE_BLOCK_CANDIDATES: ${POOL_SUBMIT_STALE_BLOCK_CANDIDATES:-false} POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: ${POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED:-true} POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD:-1} + POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: ${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0} POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD:-3} POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS:-120} POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS: ${POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS:-60} diff --git a/ops/tests/test_deployment_portability.py b/ops/tests/test_deployment_portability.py index c2e6d24..9d1fb4f 100644 --- a/ops/tests/test_deployment_portability.py +++ b/ops/tests/test_deployment_portability.py @@ -222,7 +222,7 @@ def test_compose_mounts_configured_persistent_data_paths(self) -> None: compose = (ROOT_DIR / "docker-compose.yml").read_text(encoding="utf-8") self.assertIn("postgres-data:/var/lib/postgresql/data", compose) - self.assertIn("${NODE_DATA_DIR:-node-data}:/var/lib/bdagStack/node", compose) + self.assertIn("${NODE_DATA_DIR:-./data/node}:/var/lib/bdagStack/node", compose) self.assertIn("nodeworker-data:/var/lib/bdagStack/nodeworker", compose) self.assertIn(" postgres-data:", compose) self.assertIn(" node-data:", compose) @@ -331,6 +331,10 @@ def test_installers_pin_pool_host_and_asic_lan_scope(self) -> None: self.assertIn("BDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12", env_example) self.assertIn("BDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0", env_example) self.assertIn("BDAG_ASIC_LAN_CIDRS: ${BDAG_ASIC_LAN_CIDRS:-}", compose) + self.assertIn("POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp", env_example) + self.assertIn("${BDAG_HOST_ARP_TABLE_PATH:-/proc/net/arp}:/host/proc/net/arp:ro", compose) + self.assertIn("POOL_ASIC_ARP_TABLE_PATH: ${POOL_ASIC_ARP_TABLE_PATH:-/host/proc/net/arp}", compose) + self.assertIn("POOL_ASIC_MAC_ALLOWLIST: ${POOL_ASIC_MAC_ALLOWLIST:-}", compose) self.assertIn("tr ',' ' '", entrypoint) self.assertIn('append_node_arg_once "--modules=${word}"', entrypoint) self.assertIn('set_env_value .env BDAG_ASIC_LAN_CIDRS "$scan_target"', local_installer) @@ -359,9 +363,9 @@ def test_p2p_firewall_uses_single_compose_port(self) -> None: unit = (ROOT_DIR / "ops" / "systemd" / "bdag-p2p-firewall.service").read_text(encoding="utf-8") combined = "\n".join([env_example, firewall, installer, unit]) - self.assertIn("P2P_PORT=8150", env_example) - self.assertIn('PORT="${P2P_PORT:-8150}"', firewall) - self.assertIn("Environment=P2P_PORT=8150", unit) + self.assertIn("P2P_PORT=8155", env_example) + self.assertIn('PORT="${P2P_PORT:-8155}"', firewall) + self.assertIn("Environment=P2P_PORT=8155", unit) self.assertNotIn("BDAG_P2P_PORTS", combined) self.assertNotIn("--dports", firewall) diff --git a/ops/tests/test_global_tab_resilience.py b/ops/tests/test_global_tab_resilience.py index 737fb09..5781dd0 100644 --- a/ops/tests/test_global_tab_resilience.py +++ b/ops/tests/test_global_tab_resilience.py @@ -94,8 +94,10 @@ def setUp(self) -> None: self.old_public_evm_rpc_urls = pool_ops.public_evm_rpc_urls self.old_json_rpc_call = pool_ops.json_rpc_call self.old_mining_rpc_call = pool_ops.mining_rpc_call + self.old_probe_global_display_block_height = pool_ops.probe_global_display_block_height self.old_background_maintenance_decision = pool_ops.background_maintenance_decision self.old_global_evm_fallback_enabled = pool_ops.GLOBAL_EVM_FALLBACK_ENABLED + pool_ops.probe_global_display_block_height = lambda: (None, "", {}, []) self.addCleanup(self.restore_globals) def restore_globals(self) -> None: @@ -106,6 +108,7 @@ def restore_globals(self) -> None: pool_ops.public_evm_rpc_urls = self.old_public_evm_rpc_urls pool_ops.json_rpc_call = self.old_json_rpc_call pool_ops.mining_rpc_call = self.old_mining_rpc_call + pool_ops.probe_global_display_block_height = self.old_probe_global_display_block_height pool_ops.background_maintenance_decision = self.old_background_maintenance_decision pool_ops.GLOBAL_EVM_FALLBACK_ENABLED = self.old_global_evm_fallback_enabled @@ -827,6 +830,8 @@ def setUp(self) -> None: self.old_background_maintenance_decision = pool_ops.background_maintenance_decision self.old_global_chain_rpc_urls = pool_ops.global_chain_rpc_urls self.old_mining_rpc_call = pool_ops.mining_rpc_call + self.old_probe_global_display_block_height = pool_ops.probe_global_display_block_height + pool_ops.probe_global_display_block_height = lambda: (None, "", {}, []) self.addCleanup(self.restore_globals) def restore_globals(self) -> None: @@ -835,6 +840,7 @@ def restore_globals(self) -> None: pool_ops.background_maintenance_decision = self.old_background_maintenance_decision pool_ops.global_chain_rpc_urls = self.old_global_chain_rpc_urls pool_ops.mining_rpc_call = self.old_mining_rpc_call + pool_ops.probe_global_display_block_height = self.old_probe_global_display_block_height def test_global_scan_defers_to_stale_cache_when_maintenance_backoff_blocks_work(self) -> None: cached = trusted_global_cache( diff --git a/ops/tests/test_miner_retirement_identity.py b/ops/tests/test_miner_retirement_identity.py index 9150211..553c664 100644 --- a/ops/tests/test_miner_retirement_identity.py +++ b/ops/tests/test_miner_retirement_identity.py @@ -13,6 +13,41 @@ import pool_ops # noqa: E402 +class MinerDiscoveryTests(unittest.TestCase): + def test_discover_miner_keeps_asic_visible_when_pool_api_is_wedged(self) -> None: + old_get_pools = pool_ops.get_miner_pools + old_get_status = pool_ops.get_miner_status + old_get_settings = pool_ops.get_miner_settings + self.addCleanup(lambda: setattr(pool_ops, "get_miner_pools", old_get_pools)) + self.addCleanup(lambda: setattr(pool_ops, "get_miner_status", old_get_status)) + self.addCleanup(lambda: setattr(pool_ops, "get_miner_settings", old_get_settings)) + + pool_ops.get_miner_pools = lambda ip, timeout=0: (_ for _ in ()).throw( + pool_ops.MinerAPIError("pool API timed out") + ) + pool_ops.get_miner_status = lambda ip, timeout=0: { + "model": "X100", + "hardware": "20.10.SA", + "firmware": "2.2.2", + } + pool_ops.get_miner_settings = lambda ip, timeout=0: { + "name": "2A:71:C7:F5:1F:1E", + } + + miner = pool_ops.discover_miner("192.168.1.101", timeout=0.1) + + self.assertIsNotNone(miner) + assert miner is not None + self.assertEqual(miner["ip"], "192.168.1.101") + self.assertEqual(miner["mac"], "2a:71:c7:f5:1f:1e") + self.assertEqual(miner["model"], "X100") + self.assertEqual(miner["pool_count"], 0) + self.assertEqual(miner["pools"], []) + self.assertEqual(miner["current_pool"], {}) + self.assertFalse(miner["active"]) + self.assertIn("pool API timed out", miner["pool_api_error"]) + + class MinerRetirementIdentityTests(unittest.TestCase): def setUp(self) -> None: self.tmp = tempfile.TemporaryDirectory() diff --git a/ops/tests/test_mining_appliance_preflight.py b/ops/tests/test_mining_appliance_preflight.py index d69f21d..5aa8fe5 100644 --- a/ops/tests/test_mining_appliance_preflight.py +++ b/ops/tests/test_mining_appliance_preflight.py @@ -94,6 +94,7 @@ def test_constrained_env_warnings_for_large_cache_and_expensive_defaults(self) - "BDAG_STATUS_SAMPLER_ENABLED": "0", "BDAG_ADAPTIVE_CONCURRENCY_ENABLED": "0", "BDAG_ENTRYPOINT_CHOWN_MODE": "always", + "BDAG_NODE_DEBUG_LEVEL": "info", }, profile, ) @@ -106,6 +107,29 @@ def test_constrained_env_warnings_for_large_cache_and_expensive_defaults(self) - self.assertIn("status_sampler", warnings) self.assertIn("adaptive_concurrency", warnings) self.assertIn("entrypoint_chown_mode", warnings) + self.assertIn("node_log_budget", warnings) + + def test_node_log_budget_accepts_low_noise_defaults(self) -> None: + profile = preflight.HostProfile( + os_name="linux", + arch="x86_64", + cpu_count=8, + memory_bytes=16 * preflight.GIB, + profile="standard", + kernel="test", + ) + checks = [] + preflight.check_env_defaults( + checks, + { + "BDAG_NODE_DEBUG_LEVEL": "warn", + "BDAG_NODE_NO_FILE_LOGGING": "1", + }, + profile, + ) + + found = {check.name: check for check in checks} + self.assertEqual(found["node_log_budget"].status, "pass") def test_constrained_mining_profile_accepts_disabled_node_mining(self) -> None: profile = preflight.HostProfile( diff --git a/ops/tests/test_mining_readiness_gate.py b/ops/tests/test_mining_readiness_gate.py index ce38836..9e9451a 100644 --- a/ops/tests/test_mining_readiness_gate.py +++ b/ops/tests/test_mining_readiness_gate.py @@ -275,7 +275,7 @@ def handler(method: str, _params: list[Any] | dict[str, Any], _calls: dict[str, self.assertEqual(server.url, result["backends"]["node"]["url"]) self.assertNotIn("user:pass", json.dumps(result)) - def test_reference_lag_rejects_backend(self) -> None: + def test_reference_lag_is_advisory_by_default(self) -> None: def backend_handler(method: str, _params: list[Any] | dict[str, Any], _calls: dict[str, int]) -> Any: if method == "getBlockCount": return rpc_result(1000) @@ -303,11 +303,71 @@ def reference_handler(method: str, _params: list[Any] | dict[str, Any], _calls: max_reference_lag=120, ) + self.assertTrue(result["ok"], result["failures"]) + sample = result["backends"]["node"]["samples"][0] + self.assertEqual([], sample["failures"]) + self.assertIn("reference_height_lag_121_gt_120", sample["warnings"]) + self.assertIn("reference_main_order_lag_121_gt_120", sample["warnings"]) + + def test_strict_reference_lag_rejects_backend(self) -> None: + def backend_handler(method: str, _params: list[Any] | dict[str, Any], _calls: dict[str, int]) -> Any: + if method == "getBlockCount": + return rpc_result(1000) + if method == "getTemplateHealth": + return rpc_result(healthy_health(2000)) + if method == "getBlockTemplate": + return rpc_result({"main_order": 2000, "parent": "0xparent"}) + return rpc_error(-32601, "method not found") + + def reference_handler(method: str, _params: list[Any] | dict[str, Any], _calls: dict[str, int]) -> Any: + if method == "getBlockCount": + return rpc_result(1121) + if method == "getTemplateHealth": + return rpc_result({"main_order": 2121}) + return rpc_error(-32601, "method not found") + + with FakeJsonRpcServer(backend_handler) as backend, FakeJsonRpcServer(reference_handler) as reference: + result = gate.evaluate_gate( + [gate.Backend("node", backend.url)], + reference_rpc_url=reference.url, + timeout=0.5, + sample_count=1, + sample_interval_seconds=0, + after_chain_incident=True, + max_reference_lag=120, + strict_reference_lag=True, + ) + self.assertFalse(result["ok"]) failures = result["backends"]["node"]["samples"][0]["failures"] self.assertIn("reference_height_lag_121_gt_120", failures) self.assertIn("reference_main_order_lag_121_gt_120", failures) + def test_missing_p2p_freshness_rejects_backend_before_incident_mode(self) -> None: + def handler(method: str, _params: list[Any] | dict[str, Any], _calls: dict[str, int]) -> Any: + if method == "getBlockCount": + return rpc_result(1000) + if method == "getTemplateHealth": + health = healthy_health(2000) + health.pop("p2p_mining_fresh") + return rpc_result(health) + if method == "getBlockTemplate": + return rpc_result({"main_order": 2000, "parent": "0xparent"}) + return rpc_error(-32601, "method not found") + + with FakeJsonRpcServer(handler) as backend: + result = gate.evaluate_gate( + [gate.Backend("node", backend.url)], + timeout=0.5, + sample_count=1, + sample_interval_seconds=0, + after_chain_incident=False, + ) + + self.assertFalse(result["ok"]) + failures = result["backends"]["node"]["samples"][0]["failures"] + self.assertIn("p2p_mining_fresh_missing", failures) + def test_active_node_topology_accepts_direct_backend(self) -> None: topology = gate.validate_topology( node_services=["node"], diff --git a/ops/tests/test_no_miner_collect_status.py b/ops/tests/test_no_miner_collect_status.py index a9368be..2c46d8f 100644 --- a/ops/tests/test_no_miner_collect_status.py +++ b/ops/tests/test_no_miner_collect_status.py @@ -23,6 +23,12 @@ def setUp(self) -> None: "STACK_SERVICES", "POOL_CONTAINER", "POOL_CONTAINERS", + "PROJECT_ROOT", + "POOL_ENV_FILE", + "EVM_REFERENCE_GAP_WATCH_FILE", + "POOL_PAID_WORK_STATE_FILE", + "TEMPLATE_SAFETY_STATE_FILE", + "POOL_PAID_WORK_RECENT_SECONDS", "ensure_runtime", "docker_access_error", "local_ipv4_addresses", @@ -45,7 +51,11 @@ def setUp(self) -> None: ) } self.old_time = pool_ops.time.time + self.tmpdir = tempfile.TemporaryDirectory() + pool_ops.POOL_PAID_WORK_STATE_FILE = pathlib.Path(self.tmpdir.name) / "pool-paid-work-state.json" + pool_ops.TEMPLATE_SAFETY_STATE_FILE = pathlib.Path(self.tmpdir.name) / "template-safety-state.json" self.addCleanup(self.restore_globals) + self.addCleanup(self.tmpdir.cleanup) def restore_globals(self) -> None: for name, value in self.originals.items(): @@ -209,6 +219,568 @@ def fail_if_template_probe_runs(): self.assertNotIn("live mining template probes", joined_warnings) self.assertNotIn("pool recently saw RPC connection refused", joined_warnings) + def test_no_logs_status_uses_pool_metrics_for_live_mining(self) -> None: + now = datetime(2026, 6, 20, 20, 10, 0, tzinfo=timezone.utc).timestamp() + pool_ops.time.time = lambda: now + pool_ops.NODES = ["node"] + pool_ops.OBSERVER_NODES = [] + pool_ops.STACK_SERVICES = ["postgres", "node", "pool"] + pool_ops.SERVICES = list(pool_ops.STACK_SERVICES) + pool_ops.POOL_CONTAINER = "pool" + pool_ops.POOL_CONTAINERS = ["pool"] + pool_ops.ensure_runtime = lambda: None + pool_ops.docker_access_error = lambda: None + pool_ops.local_ipv4_addresses = lambda: ["192.168.1.100"] + pool_ops.default_miner_pool_settings = lambda: { + "pool_url": "stratum+tcp://192.168.1.100:3334", + "worker_user": "0x0000000000000000000000000000000000000000", + "pool_password": "x", + } + pool_ops.run = lambda command, timeout=20: pool_ops.CommandResult(command, 0, "", "", 0.0) + pool_ops.read_latest_action = lambda: None + pool_ops.discover_observer_node_services = lambda: [] + pool_ops.docker_top = lambda _name: ( + "UID PID PPID C STIME TTY TIME CMD\n" + "root 1 0 0 20:10 ? 00:00:01 /usr/local/bin/bdag\n" + ) + pool_ops.docker_logs = lambda _name, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.docker_logs_many = lambda _names, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.collect_miner_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not scan miner APIs") + ) + pool_ops.collect_template_probe_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not run template probes") + ) + pool_ops.collect_host_pressure = lambda: { + "iowait_percent": 0.0, + "io_some_avg10": 0.0, + "io_full_avg10": 0.0, + "iowait_warning_active": False, + "samples": [], + } + pool_ops.docker_inspect = lambda names: { + name: { + "name": name, + "image": "test", + "running": True, + "status": "running", + "restart_count": 0, + "exit_code": 0, + "error": "", + "ports": {}, + } + for name in names + } + metrics_calls = [] + + def fake_metrics(_containers): + metrics_calls.append(True) + return { + "generated_at": "2026-06-20T20:10:00+0000", + "status": "ok", + "active_connections": 3.0, + "selected_backend": "node", + "block_submit_outcomes": {"accepted:ok": 8}, + "blocks": {"found": 8}, + "shares_accepted_total": 64.0, + "source_job_health": {"ok": True, "authorized_miners": 3, "ready_miners": 3}, + "source_backend_health": { + "node": { + "healthy": True, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, + "ws_connected": True, + } + }, + "selected_backend_source_health": { + "healthy": True, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, + "ws_connected": True, + }, + "template_conversion_stall": {}, + "loss_ledger": {}, + } + + pool_ops.collect_pool_prometheus_metrics = fake_metrics + pool_ops.collect_sync_progress = lambda: { + "status": "syncing", + "percent": 99.8, + "current_block": 11_711_000, + "highest_block": 11_725_000, + "remaining_blocks": 14_000, + "source": "nodes", + "error": "eth_syncing active", + "nodes": { + "node": { + "status": "syncing", + "percent": 99.8, + "current_block": 11_711_000, + "highest_block": 11_725_000, + "remaining_blocks": 14_000, + "source": "node:eth_syncing", + "error": "eth_syncing active", + "chain_block_count": 12_070_000, + "chain_main_height": 9_249_000, + "chain_rpc_source": "getBlockCount", + "chain_rpc_latency_ms": 3.3, + "chain_rpc_attempts": 1, + "chain_rpc_retry_limit": 2, + "chain_rpc_error": "", + } + }, + } + pool_ops.observe_sync_progress_health = lambda _sync_progress: { + "active_nodes": ["node"], + "active_node_count": 1, + "node_rates_blocks_per_second": {"node": 1.2}, + "lookback_seconds": 2700, + } + pool_ops.read_sync_coordinator_state = lambda: {} + + status = pool_ops.collect_status(include_logs=False) + + self.assertTrue(metrics_calls) + self.assertEqual(status["overall"], "ok", status["sync_warnings"]) + self.assertEqual(status["mode"], "mining") + self.assertTrue(status["can_accept_shares"]) + self.assertTrue(status["can_submit_blocks"]) + self.assertTrue(status["can_mine"]) + self.assertEqual(status["sync_warnings"], []) + self.assertTrue(status["sync_health"]["selected_backend_mining_safe"]) + self.assertEqual(status["sync_health"]["pool_metrics_accepted_block_submissions"], 8) + + def test_no_logs_status_allows_submit_on_native_safe_small_peer_lead_without_recent_paid_work(self) -> None: + now = datetime(2026, 6, 21, 6, 55, 0, tzinfo=timezone.utc).timestamp() + pool_ops.time.time = lambda: now + pool_ops.NODES = ["node"] + pool_ops.OBSERVER_NODES = [] + pool_ops.STACK_SERVICES = ["postgres", "node", "pool"] + pool_ops.SERVICES = list(pool_ops.STACK_SERVICES) + pool_ops.POOL_CONTAINER = "pool" + pool_ops.POOL_CONTAINERS = ["pool"] + pool_ops.ensure_runtime = lambda: None + pool_ops.docker_access_error = lambda: None + pool_ops.local_ipv4_addresses = lambda: ["192.168.1.100"] + pool_ops.default_miner_pool_settings = lambda: { + "pool_url": "stratum+tcp://192.168.1.100:3334", + "worker_user": "0x0000000000000000000000000000000000000000", + "pool_password": "x", + } + pool_ops.run = lambda command, timeout=20: pool_ops.CommandResult(command, 0, "", "", 0.0) + pool_ops.read_latest_action = lambda: None + pool_ops.discover_observer_node_services = lambda: [] + pool_ops.docker_top = lambda _name: ( + "UID PID PPID C STIME TTY TIME CMD\n" + "root 1 0 0 20:10 ? 00:00:01 /usr/local/bin/bdag\n" + ) + pool_ops.docker_logs = lambda _name, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.docker_logs_many = lambda _names, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.collect_miner_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not scan miner APIs") + ) + pool_ops.collect_template_probe_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not run template probes") + ) + pool_ops.collect_host_pressure = lambda: { + "iowait_percent": 0.0, + "io_some_avg10": 0.0, + "io_full_avg10": 0.0, + "iowait_warning_active": False, + "samples": [], + } + pool_ops.docker_inspect = lambda names: { + name: { + "name": name, + "image": "test", + "running": True, + "status": "running", + "restart_count": 0, + "exit_code": 0, + "error": "", + "ports": {}, + } + for name in names + } + pool_ops.collect_pool_prometheus_metrics = lambda _containers: { + "generated_at": "2026-06-21T06:55:00+0000", + "status": "ok", + "active_connections": 4.0, + "selected_backend": "node", + "block_submit_outcomes": {}, + "blocks": {"found": 0}, + "shares_accepted_total": 12.0, + "source_job_health": {"ok": True, "authorized_miners": 4, "ready_miners": 4}, + "source_backend_health": { + "node": { + "healthy": True, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 4, + "ws_connected": True, + } + }, + "selected_backend_source_health": { + "healthy": True, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 4, + "ws_connected": True, + }, + "template_conversion_stall": {}, + "loss_ledger": {}, + } + pool_ops.collect_sync_progress = lambda: { + "status": "syncing", + "percent": 99.9, + "current_block": 12_124_000, + "highest_block": 12_124_004, + "remaining_blocks": 4, + "source": "nodes", + "error": "eth_syncing active", + "chain_syncing": True, + "nodes": { + "node": { + "status": "syncing", + "percent": 99.9, + "current_block": 12_124_000, + "highest_block": 12_124_004, + "remaining_blocks": 4, + "source": "node:eth_syncing", + "error": "eth_syncing active", + "chain_syncing": True, + "chain_block_count": 12_124_000, + "chain_main_height": 12_124_000, + "chain_rpc_source": "getBlockCount", + "chain_rpc_latency_ms": 3.3, + "chain_rpc_attempts": 1, + "chain_rpc_retry_limit": 2, + "chain_rpc_error": "", + } + }, + } + pool_ops.observe_sync_progress_health = lambda _sync_progress: { + "active_nodes": [], + "active_node_count": 0, + "node_rates_blocks_per_second": {}, + "lookback_seconds": 2700, + } + pool_ops.read_sync_coordinator_state = lambda: {} + + status = pool_ops.collect_status(include_logs=False) + + self.assertEqual(status["overall"], "ok", status["sync_warnings"]) + self.assertEqual(status["mode"], "mining") + self.assertTrue(status["can_submit_blocks"]) + self.assertTrue(status["can_mine"]) + self.assertEqual(status["sync_warnings"], []) + self.assertFalse(status["sync_health"]["pool_has_recent_paid_work"]) + self.assertTrue(status["sync_health"]["selected_backend_raw_mining_safe"]) + self.assertTrue(status["sync_health"]["selected_backend_native_p2p_current_safe"]) + self.assertTrue(status["sync_health"]["native_chain_progress_safe"]) + joined_maintenance = "\n".join(status["maintenance_warnings"]) + self.assertIn("selected pool backend is still catching up by 4 blocks", joined_maintenance) + self.assertIn("selected backend is mineable, submit-ready, and native P2P fresh", joined_maintenance) + + def test_no_logs_status_treats_raw_backend_flicker_as_advisory_when_paid_work_is_fresh(self) -> None: + now = datetime(2026, 6, 21, 3, 45, 0, tzinfo=timezone.utc).timestamp() + pool_ops.time.time = lambda: now + pool_ops.NODES = ["node"] + pool_ops.OBSERVER_NODES = [] + pool_ops.STACK_SERVICES = ["postgres", "node", "pool"] + pool_ops.SERVICES = list(pool_ops.STACK_SERVICES) + pool_ops.POOL_CONTAINER = "pool" + pool_ops.POOL_CONTAINERS = ["pool"] + pool_ops.ensure_runtime = lambda: None + pool_ops.docker_access_error = lambda: None + pool_ops.local_ipv4_addresses = lambda: ["192.168.1.100"] + pool_ops.default_miner_pool_settings = lambda: { + "pool_url": "stratum+tcp://192.168.1.100:3334", + "worker_user": "0x0000000000000000000000000000000000000000", + "pool_password": "x", + } + pool_ops.run = lambda command, timeout=20: pool_ops.CommandResult(command, 0, "", "", 0.0) + pool_ops.read_latest_action = lambda: None + pool_ops.discover_observer_node_services = lambda: [] + pool_ops.docker_top = lambda _name: ( + "UID PID PPID C STIME TTY TIME CMD\n" + "root 1 0 0 20:10 ? 00:00:01 /usr/local/bin/bdag\n" + ) + pool_ops.docker_logs = lambda _name, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.docker_logs_many = lambda _names, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.collect_miner_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not scan miner APIs") + ) + pool_ops.collect_template_probe_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not run template probes") + ) + pool_ops.collect_host_pressure = lambda: { + "iowait_percent": 0.0, + "io_some_avg10": 0.0, + "io_full_avg10": 0.0, + "iowait_warning_active": False, + "samples": [], + } + pool_ops.docker_inspect = lambda names: { + name: { + "name": name, + "image": "test", + "running": True, + "status": "running", + "restart_count": 0, + "exit_code": 0, + "error": "", + "ports": {}, + } + for name in names + } + pool_ops.collect_pool_prometheus_metrics = lambda _containers: { + "generated_at": "2026-06-21T03:45:00+0000", + "status": "ok", + "active_connections": 4.0, + "selected_backend": "node", + "block_submit_outcomes": {"accepted:ok": 17}, + "blocks": {"found": 17}, + "shares_accepted_total": 256.0, + "source_job_health": {"ok": False, "authorized_miners": 0, "ready_miners": 0}, + "source_backend_health": { + "node": { + "healthy": True, + "node_mineable": False, + "node_submit_ready": False, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 4, + "ws_connected": True, + } + }, + "selected_backend_source_health": { + "healthy": True, + "node_mineable": False, + "node_submit_ready": False, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 4, + "ws_connected": True, + }, + "template_conversion_stall": {}, + "loss_ledger": {}, + } + pool_ops.collect_sync_progress = lambda: { + "status": "syncing", + "percent": 99.8, + "current_block": 11_711_000, + "highest_block": 11_725_000, + "remaining_blocks": 14_000, + "source": "nodes", + "error": "eth_syncing active", + "chain_syncing": True, + "nodes": { + "node": { + "status": "syncing", + "percent": 99.8, + "current_block": 11_711_000, + "highest_block": 11_725_000, + "remaining_blocks": 14_000, + "source": "node:eth_syncing", + "error": "eth_syncing active", + "chain_syncing": True, + "chain_block_count": 12_070_000, + "chain_main_height": 9_249_000, + "chain_rpc_source": "getBlockCount", + "chain_rpc_latency_ms": 3.3, + "chain_rpc_attempts": 1, + "chain_rpc_retry_limit": 2, + "chain_rpc_error": "", + } + }, + } + pool_ops.observe_sync_progress_health = lambda _sync_progress: { + "active_nodes": [], + "active_node_count": 0, + "node_rates_blocks_per_second": {}, + "lookback_seconds": 2700, + } + pool_ops.read_sync_coordinator_state = lambda: {} + + status = pool_ops.collect_status(include_logs=False) + + self.assertEqual(status["overall"], "ok", status["sync_warnings"]) + self.assertEqual(status["mode"], "mining") + self.assertTrue(status["can_submit_blocks"]) + self.assertTrue(status["can_mine"]) + self.assertEqual(status["sync_warnings"], []) + self.assertFalse(status["sync_health"]["selected_backend_raw_mining_safe"]) + self.assertTrue(status["sync_health"]["selected_backend_recent_paid_work_safe"]) + self.assertTrue(status["sync_health"]["selected_backend_mining_safe"]) + self.assertTrue(status["sync_health"]["pool_paid_work_state"]["accepted_block_recent"]) + self.assertEqual(status["sync_progress"]["status"], "synced") + self.assertFalse(status["sync_progress"]["chain_syncing"]) + self.assertTrue(status["sync_progress"]["evm_chain_syncing"]) + self.assertTrue(status["sync_progress"]["mining_advisory_sync"]) + joined_maintenance = "\n".join(status["maintenance_warnings"]) + self.assertIn("accepted block submission remains fresh", joined_maintenance) + self.assertIn("mineable=false", joined_maintenance) + self.assertIn("submit_ready=false", joined_maintenance) + + def test_no_logs_status_uses_native_progress_when_backend_health_sample_is_missing(self) -> None: + now = datetime(2026, 6, 21, 4, 0, 0, tzinfo=timezone.utc).timestamp() + pool_ops.time.time = lambda: now + pool_ops.NODES = ["node"] + pool_ops.OBSERVER_NODES = [] + pool_ops.STACK_SERVICES = ["postgres", "node", "pool"] + pool_ops.SERVICES = list(pool_ops.STACK_SERVICES) + pool_ops.POOL_CONTAINER = "pool" + pool_ops.POOL_CONTAINERS = ["pool"] + pool_ops.ensure_runtime = lambda: None + pool_ops.docker_access_error = lambda: None + pool_ops.local_ipv4_addresses = lambda: ["192.168.1.100"] + pool_ops.default_miner_pool_settings = lambda: { + "pool_url": "stratum+tcp://192.168.1.100:3334", + "worker_user": "0x0000000000000000000000000000000000000000", + "pool_password": "x", + } + pool_ops.run = lambda command, timeout=20: pool_ops.CommandResult(command, 0, "", "", 0.0) + pool_ops.read_latest_action = lambda: None + pool_ops.discover_observer_node_services = lambda: [] + pool_ops.docker_top = lambda _name: ( + "UID PID PPID C STIME TTY TIME CMD\n" + "root 1 0 0 20:10 ? 00:00:01 /usr/local/bin/bdag\n" + ) + pool_ops.docker_logs = lambda _name, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.docker_logs_many = lambda _names, lines=160: (_ for _ in ()).throw( + AssertionError("no-logs collection must not read docker logs") + ) + pool_ops.collect_miner_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not scan miner APIs") + ) + pool_ops.collect_template_probe_health = lambda: (_ for _ in ()).throw( + AssertionError("no-logs collection must not run template probes") + ) + pool_ops.collect_host_pressure = lambda: { + "iowait_percent": 0.0, + "io_some_avg10": 0.0, + "io_full_avg10": 0.0, + "iowait_warning_active": False, + "samples": [], + } + pool_ops.docker_inspect = lambda names: { + name: { + "name": name, + "image": "test", + "running": True, + "status": "running", + "restart_count": 0, + "exit_code": 0, + "error": "", + "ports": {}, + } + for name in names + } + pool_ops.collect_pool_prometheus_metrics = lambda _containers: { + "generated_at": "2026-06-21T04:00:00+0000", + "status": "ok", + "active_connections": 4.0, + "selected_backend": "node", + "block_submit_outcomes": {"accepted:ok": 23}, + "blocks": {"found": 23}, + "shares_accepted_total": 512.0, + "source_job_health": {"ok": False, "authorized_miners": 0, "ready_miners": 0}, + "source_backend_health": {}, + "selected_backend_source_health": {}, + "template_conversion_stall": {}, + "loss_ledger": {}, + } + pool_ops.collect_sync_progress = lambda: { + "status": "synced", + "percent": 100.0, + "current_block": 12_097_278, + "highest_block": 12_097_278, + "remaining_blocks": 0, + "source": "nodes:native-template-health", + "error": "", + "native_is_current": True, + "chain_syncing": False, + "evm_chain_syncing": True, + "mining_advisory_sync": True, + "nodes": { + "node": { + "status": "synced", + "percent": 100.0, + "current_block": 12_097_278, + "highest_block": 12_097_278, + "remaining_blocks": 0, + "source": "node:native-template-health", + "error": "", + "native_is_current": True, + "chain_syncing": False, + "evm_chain_syncing": True, + "mining_advisory_sync": True, + "chain_block_count": 12_097_278, + "chain_main_height": 9_249_000, + "chain_rpc_source": "getBlockCount", + "chain_rpc_latency_ms": 3.3, + "chain_rpc_attempts": 1, + "chain_rpc_retry_limit": 2, + "chain_rpc_error": "", + } + }, + } + pool_ops.observe_sync_progress_health = lambda _sync_progress: { + "active_nodes": [], + "active_node_count": 0, + "node_rates_blocks_per_second": {}, + "lookback_seconds": 2700, + } + pool_ops.read_sync_coordinator_state = lambda: {} + pool_ops.EVM_REFERENCE_GAP_WATCH_FILE = pathlib.Path(self.tmpdir.name) / "evm-gap-watch.json" + pool_ops.EVM_REFERENCE_GAP_WATCH_FILE.write_text( + '{"restore_required": true, "reason": "EVM reference gap stalled"}', + encoding="utf-8", + ) + + status = pool_ops.collect_status(include_logs=False) + + self.assertEqual(status["overall"], "ok", status["sync_warnings"]) + self.assertEqual(status["mode"], "mining") + self.assertTrue(status["can_submit_blocks"]) + self.assertTrue(status["can_mine"]) + self.assertFalse(status["sync_health"]["selected_backend_raw_mining_safe"]) + self.assertFalse(status["sync_health"]["selected_backend_recent_paid_work_safe"]) + self.assertTrue(status["sync_health"]["native_progress_paid_work_safe"]) + self.assertTrue(status["sync_health"]["readiness_override_safe"]) + self.assertTrue(status["sync_health"]["selected_backend_mining_safe"]) + self.assertEqual(status["sync_warnings"], []) + self.assertTrue(status["sync_health"]["evm_reference_gap_advisory"]) + self.assertTrue(status["sync_health"]["evm_reference_gap_restore_suppressed_by_native_paid_work"]) + self.assertFalse(status["sync_health"]["evm_reference_gap_watch"]["restore_required"]) + self.assertTrue(status["sync_health"]["evm_reference_gap_watch"]["would_restore_required"]) + self.assertNotIn("needs_chain_data_restore", status["sync_health"]) + def test_no_miner_status_promotes_busy_syncing_to_syncing(self) -> None: now = datetime(2026, 5, 25, 12, 0, 0, tzinfo=timezone.utc).timestamp() pool_ops.time.time = lambda: now @@ -581,6 +1153,8 @@ def fake_inspect(names): "node_mineable": True, "node_submit_ready": True, "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, "ws_connected": True, }, "template_conversion_stall": {}, @@ -673,6 +1247,32 @@ def test_miner_failures_are_advisory_with_active_mining_evidence(self) -> None: ) ) + def test_paid_work_state_expires_without_new_accepted_blocks(self) -> None: + pool_ops.POOL_PAID_WORK_RECENT_SECONDS = 60.0 + first = pool_ops.update_pool_paid_work_state(12.0, now_epoch=1000.0) + stale = pool_ops.update_pool_paid_work_state(12.0, now_epoch=1061.0) + refreshed = pool_ops.update_pool_paid_work_state(13.0, now_epoch=1062.0) + + self.assertTrue(first["accepted_block_recent"]) + self.assertFalse(stale["accepted_block_recent"]) + self.assertTrue(refreshed["accepted_block_recent"]) + self.assertEqual(refreshed["accepted_block_submission_delta"], 1) + + def test_paid_work_state_ages_but_does_not_clear_on_metrics_unavailable(self) -> None: + pool_ops.POOL_PAID_WORK_RECENT_SECONDS = 60.0 + first = pool_ops.update_pool_paid_work_state(12.0, now_epoch=1000.0) + missing_metrics = pool_ops.update_pool_paid_work_state(0.0, now_epoch=1010.0, metrics_available=False) + stale_missing_metrics = pool_ops.update_pool_paid_work_state(0.0, now_epoch=1061.0, metrics_available=False) + confirmed_zero = pool_ops.update_pool_paid_work_state(0.0, now_epoch=1062.0, metrics_available=True) + + self.assertTrue(first["accepted_block_recent"]) + self.assertTrue(missing_metrics["accepted_block_recent"]) + self.assertEqual(missing_metrics["accepted_block_submissions"], 12) + self.assertFalse(missing_metrics["metrics_available"]) + self.assertFalse(stale_missing_metrics["accepted_block_recent"]) + self.assertFalse(confirmed_zero["accepted_block_recent"]) + self.assertIsNone(confirmed_zero["last_accepted_at_epoch"]) + class EffectiveMinerDemandTests(unittest.TestCase): def test_pool_metrics_count_as_connected_miner_demand(self) -> None: @@ -1011,6 +1611,25 @@ def test_rawdatadir_sidecar_can_run_when_pool_is_intentionally_not_ready(self) - self.assertFalse(decision["pool_ready_required"]) self.assertEqual([], decision["reasons"]) + def test_docker_compose_command_includes_override_file_when_present(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + root = pathlib.Path(tmp) + pool_ops.PROJECT_ROOT = root + pool_ops.POOL_ENV_FILE = root / ".env" + (root / ".env").write_text("", encoding="utf-8") + (root / "docker-compose.yml").write_text("services: {}\n", encoding="utf-8") + (root / "docker-compose.override.yml").write_text("services: {}\n", encoding="utf-8") + + command = pool_ops.docker_compose_command("up", "node") + + compose_files = [ + command[index + 1] + for index, value in enumerate(command) + if value == "-f" and index + 1 < len(command) + ] + self.assertIn(str(root / "docker-compose.yml"), compose_files) + self.assertIn(str(root / "docker-compose.override.yml"), compose_files) + def test_lazy_archive_task_defers_on_load_pressure(self) -> None: pool_ops.BACKGROUND_MAINTENANCE_BACKOFF_ENABLED = True pool_ops.BACKGROUND_MAINTENANCE_LAZY_TASKS = {"global_scan"} diff --git a/ops/tests/test_nodeworker_entrypoint.py b/ops/tests/test_nodeworker_entrypoint.py index 819dbeb..655a3ad 100644 --- a/ops/tests/test_nodeworker_entrypoint.py +++ b/ops/tests/test_nodeworker_entrypoint.py @@ -67,12 +67,42 @@ def assert_stdout_contains(self, result: subprocess.CompletedProcess[str], needl def test_print_mode_reports_node_args_append(self) -> None: result = self.run_entrypoint({"NODE_ARGS_APPEND": "--miner --maxpeers=160"}) - self.assert_stdout_contains(result, "NODE_ARGS_APPEND=--miner --maxpeers=160") + self.assert_stdout_contains( + result, + "NODE_ARGS_APPEND=--miner --maxpeers=160 --debuglevel=warn --nofilelogging", + ) - def test_print_mode_reports_empty_node_args_append(self) -> None: + def test_print_mode_reports_default_low_io_node_log_args(self) -> None: result = self.run_entrypoint({}) - self.assert_stdout_contains(result, "NODE_ARGS_APPEND=") + self.assert_stdout_contains(result, "NODE_ARGS_APPEND=--debuglevel=warn --nofilelogging") + + def test_print_mode_appends_mining_template_safety_args(self) -> None: + result = self.run_entrypoint( + { + "BDAG_NODE_OBSOLETE_HEIGHT": "20", + "BDAG_NODE_MINING_NO_PENDING_TX": "1", + } + ) + + self.assert_stdout_contains(result, "--obsoleteheight=20") + self.assert_stdout_contains(result, "--miningnopendingtx") + self.assert_stdout_contains(result, "--debuglevel=warn") + + def test_print_mode_keeps_operator_mining_template_overrides(self) -> None: + result = self.run_entrypoint( + { + "NODE_ARGS_APPEND": "--obsoleteheight=7 --miningnopendingtx", + "BDAG_NODE_OBSOLETE_HEIGHT": "20", + "BDAG_NODE_MINING_NO_PENDING_TX": "1", + } + ) + + self.assert_stdout_contains( + result, + "NODE_ARGS_APPEND=--obsoleteheight=7 --miningnopendingtx --debuglevel=warn --nofilelogging", + ) + self.assertNotIn("--obsoleteheight=20", result.stdout) def test_print_mode_does_not_emit_removed_sync_flags(self) -> None: result = self.run_entrypoint( @@ -82,11 +112,25 @@ def test_print_mode_does_not_emit_removed_sync_flags(self) -> None: } ) - self.assert_stdout_contains(result, "NODE_ARGS_APPEND=--cache=1024") + self.assert_stdout_contains( + result, + "NODE_ARGS_APPEND=--cache=1024 --debuglevel=warn --nofilelogging", + ) combined = result.stdout + result.stderr self.assertNotIn("FAST", combined.upper()) self.assertEqual("", result.stderr) + def test_print_mode_allows_operator_debug_level_override(self) -> None: + result = self.run_entrypoint( + { + "BDAG_NODE_DEBUG_LEVEL": "error", + "BDAG_NODE_NO_FILE_LOGGING": "0", + } + ) + + self.assert_stdout_contains(result, "NODE_ARGS_APPEND=--debuglevel=error") + self.assertNotIn("--nofilelogging", result.stdout) + def test_node_mining_env_appends_guard_args_without_forcing_rpc_module(self) -> None: result = self.run_entrypoint( { @@ -100,6 +144,26 @@ def test_node_mining_env_appends_guard_args_without_forcing_rpc_module(self) -> self.assertNotIn("--fastartifactsync", result.stdout) self.assert_stdout_contains(result, "--miner") self.assert_stdout_contains(result, "--miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc") + self.assert_stdout_contains(result, "--debuglevel=warn") + self.assert_stdout_contains(result, "--nofilelogging") + self.assertNotIn("--allowminingwhennearlysynced", result.stdout) + self.assertNotIn("--allowsubmitwhennotsynced", result.stdout) + + def test_node_mining_env_derives_args_from_pool_address(self) -> None: + result = self.run_entrypoint( + { + "BDAG_ENABLE_NODE_MINING": "1", + "BDAG_NODE_MODULES": "Blockdag,miner", + "BDAG_NODE_MINING_ARGS": "", + "MINING_POOL_ADDRESS": "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc", + } + ) + + self.assert_stdout_contains(result, "--miner") + self.assert_stdout_contains(result, "--miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc") + self.assert_stdout_contains(result, "--modules=Blockdag") + self.assert_stdout_contains(result, "--modules=miner") + self.assertNotIn("modules=" + "p2p", result.stdout) self.assertNotIn("--allowminingwhennearlysynced", result.stdout) self.assertNotIn("--allowsubmitwhennotsynced", result.stdout) @@ -116,6 +180,21 @@ def test_node_mining_env_allows_blockdag_and_miner_rpc_modules(self) -> None: self.assert_stdout_contains(result, "--modules=Blockdag") self.assert_stdout_contains(result, "--modules=miner") + self.assertNotIn("modules=" + "p2p", result.stdout) + + def test_node_mining_env_rejects_forbidden_peer_rpc_module(self) -> None: + result = self.run_entrypoint( + { + "BDAG_ENABLE_NODE_MINING": "1", + "BDAG_NODE_MODULES": "Blockdag,miner," + "p2p", + "BDAG_NODE_MINING_ARGS": ( + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + ), + } + ) + + self.assertNotEqual(result.returncode, 0) + self.assertIn("forbidden peer module", result.stderr) def test_entrypoint_prepares_runtime_config_before_privilege_drop(self) -> None: entrypoint = ENTRYPOINT.read_text(encoding="utf-8") @@ -139,7 +218,7 @@ def test_runtime_config_normalizes_config_port_to_p2p_port_before_start(self) -> runuser_index = entrypoint.index("exec runuser -u bdagStack -g bdagStack -- \"$@\"") self.assertIn("configured_p2p_port()", entrypoint) - self.assertIn("local port=\"${P2P_PORT:-8150}\"", entrypoint) + self.assertIn("local port=\"${P2P_PORT:-8155}\"", entrypoint) self.assertIn("valid_tcp_port", entrypoint) self.assertIn("set_config_value \"$runtime_config\" port \"$desired_port\"", entrypoint) self.assertIn("node config P2P port ${current_port:-} differs", entrypoint) @@ -224,7 +303,7 @@ def test_runtime_config_copy_overrides_stale_p2p_port_without_touching_mount(sel "BDAG_EPHEMERAL_DIR": str(runtime_dir), "BDAG_ENTRYPOINT_CHOWN_MODE": "never", "FAKE_BDAGSTACK_CAN_READ_CONFIG": "1", - "P2P_PORT": "8150", + "P2P_PORT": "8155", } result = subprocess.run( [ @@ -241,8 +320,8 @@ def test_runtime_config_copy_overrides_stale_p2p_port_without_touching_mount(sel ) self.assertEqual(result.returncode, 0, result.stderr) - self.assertIn("node config P2P port 8154 differs from P2P_PORT=8150", result.stderr) - self.assertRegex(runtime_config.read_text(encoding="utf-8"), r"(?m)^port=8150$") + self.assertIn("node config P2P port 8154 differs from P2P_PORT=8155", result.stderr) + self.assertRegex(runtime_config.read_text(encoding="utf-8"), r"(?m)^port=8155$") self.assertRegex(config_file.read_text(encoding="utf-8"), r"(?m)^port=8154$") diff --git a/ops/tests/test_optimization_measurement.py b/ops/tests/test_optimization_measurement.py index 097c887..341b9cc 100644 --- a/ops/tests/test_optimization_measurement.py +++ b/ops/tests/test_optimization_measurement.py @@ -16,11 +16,22 @@ def test_flatten_status_sample_extracts_resource_and_sync_fields(self) -> None: "overall": "syncing", "mode": "sync_only_no_miners", "can_mine": False, + "can_accept_shares": True, + "can_submit_blocks": False, + "age_seconds": 2.0, + "fresh": True, + "status_sampler": {"hit": True, "age_seconds": 1.5}, "sync_progress": { "status": "syncing", "current_block": 10, "highest_block": 15, + "current_block_source": "eth_syncing", "remaining_blocks": 5, + "native_is_current": True, + "chain_syncing": True, + "mining_advisory_sync": True, + "p2p_connections": 8, + "p2p_network_gap": 0, "nodes": { "primary": {"chain_rpc_latency_ms": 12.5}, "secondary": {"chain_rpc_latency_ms": 8.0}, @@ -39,10 +50,102 @@ def test_flatten_status_sample_extracts_resource_and_sync_fields(self) -> None: self.assertEqual(sample["sync_status"], "syncing") self.assertEqual(sample["current_block"], 10) + self.assertEqual(sample["current_block_source"], "eth_syncing") self.assertEqual(sample["remaining_blocks"], 5) + self.assertTrue(sample["native_is_current"]) + self.assertTrue(sample["chain_syncing"]) + self.assertTrue(sample["mining_advisory_sync"]) + self.assertEqual(sample["p2p_connections"], 8) + self.assertEqual(sample["p2p_network_gap"], 0) + self.assertTrue(sample["can_accept_shares"]) + self.assertFalse(sample["can_submit_blocks"]) self.assertEqual(sample["chain_rpc_latency_ms_max"], 12.5) self.assertEqual(sample["adaptive_workers"]["global_rpc"], 6) self.assertEqual(sample["dashboard_latency_ms"], 3.1) + self.assertEqual(sample["status_age_seconds"], 2.0) + self.assertTrue(sample["status_fresh"]) + self.assertTrue(sample["status_sampler_hit"]) + self.assertEqual(sample["status_sampler_age_seconds"], 1.5) + + def test_pool_metrics_parser_extracts_mining_quality_fields(self) -> None: + metrics = measurement.parse_prometheus_metrics( + """ +pool_active_connections{pool_id="0"} 2 +pool_job_health_ready_miners{pool_id="0"} 2 +pool_block_submit_outcomes_total{outcome="accepted",pool_id="0",reason="ok"} 38 +pool_block_submit_outcomes_total{outcome="rejected",pool_id="0",reason="tip-overdue"} 1 +pool_block_submit_outcomes_total{outcome="rejected-local",pool_id="0",reason="stale-parent"} 2 +pool_blocks_found_total{pool_id="0"} 41 +pool_shares_accepted_total{pool_id="0"} 156 +pool_shares_rejected_total{pool_id="0",reason="invalidated_job"} 38 +pool_shares_rejected_total{pool_id="0",reason="non_current_job"} 4 +pool_rpc_backend_node_health_mineable{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_submit_ready{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_p2p_mining_fresh{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count{node="node",pool_id="0"} 4 +pool_rpc_backend_node_health_template_invalidations_total{cause="parent-changed",node="node",pool_id="0"} 5 +pool_clean_template_refresh_events_total{event="broadcast",pool_id="0"} 8 +pool_clean_template_refresh_events_total{event="readiness_hold",pool_id="0"} 2 +pool_template_conversion_stall_failure_ratio{pool_id="0"} 7.5 +pool_template_conversion_stall_window_candidates{kind="accepted",pool_id="0"} 38 +pool_template_conversion_stall_window_candidates{kind="failed",pool_id="0"} 3 +pool_template_conversion_stall_window_candidates{kind="total",pool_id="0"} 41 +""" + ) + + sample = measurement.flatten_pool_metrics(metrics, latency_ms=2.5) + + self.assertEqual(sample["pool_active_connections"], 2) + self.assertEqual(sample["pool_job_health_ready_miners"], 2) + self.assertEqual(sample["pool_block_submit_accepted_total"], 38) + self.assertEqual(sample["pool_block_submit_rejected_total"], 3) + self.assertEqual(sample["pool_block_submit_rejected_by_reason"], {"stale-parent": 2, "tip-overdue": 1}) + self.assertEqual(sample["pool_share_reject_by_reason"], {"invalidated_job": 38, "non_current_job": 4}) + self.assertEqual(sample["pool_backend_mineable"], 1) + self.assertEqual(sample["pool_backend_submit_ready"], 1) + self.assertEqual(sample["pool_backend_p2p_mining_fresh"], 1) + self.assertEqual(sample["pool_backend_p2p_fresh_consensus_peer_count"], 4) + self.assertEqual(sample["pool_template_invalidation_by_cause"], {"parent-changed": 5}) + self.assertEqual(sample["pool_clean_refresh_by_event"], {"broadcast": 8, "readiness_hold": 2}) + self.assertEqual(sample["pool_template_conversion_failure_ratio"], 7.5) + self.assertEqual(sample["pool_template_conversion_window_total"], 41) + + def test_flatten_status_sample_infers_current_block_source(self) -> None: + payload = { + "sync_progress": { + "current_block": 42, + "highest_block": 50, + "nodes": { + "node": { + "current_block": 42, + "current_block_source": "eth_syncing", + } + }, + }, + "adaptive_concurrency": {"workers": {}}, + } + + sample = measurement.flatten_status_sample(payload, "fixture", 1.0) + + self.assertEqual(sample["current_block"], 42) + self.assertEqual(sample["current_block_source"], "eth_syncing") + + def test_flatten_status_sample_marks_native_chain_count_source(self) -> None: + payload = { + "sync_progress": { + "current_block": 121, + "highest_block": 121, + "chain_block_count": 121, + "native_is_current": True, + "source": "native-rpc", + }, + "adaptive_concurrency": {"workers": {}}, + } + + sample = measurement.flatten_status_sample(payload, "fixture", 1.0) + + self.assertEqual(sample["current_block"], 121) + self.assertEqual(sample["current_block_source"], "native-rpc") def test_summarize_samples_reports_block_rate_and_worker_ranges(self) -> None: samples = [ @@ -52,16 +155,43 @@ def test_summarize_samples_reports_block_rate_and_worker_ranges(self) -> None: "source": "fixture", "overall": "syncing", "mode": "sync_only_no_miners", + "can_mine": False, + "can_submit_blocks": False, "sync_status": "syncing", "current_block": 1000, + "current_block_source": "native", "remaining_blocks": 50, + "native_is_current": False, + "chain_syncing": True, + "mining_advisory_sync": False, + "p2p_connections": 6, + "p2p_network_gap": 3, "connected_miners": 0, "managed_miners": 0, "collection_ms": 10.0, + "status_age_seconds": 1.0, + "status_sampler_hit": True, "chain_rpc_latency_ms_max": 5.0, "iowait_percent": 1.0, "adaptive_workers": {"global_rpc": 6}, "host_profile": {"profile": "pi5", "os": "linux", "arch": "arm64"}, + "pool_active_connections": 2, + "pool_job_health_ready_miners": 2, + "pool_backend_mineable": 1, + "pool_backend_submit_ready": 1, + "pool_backend_p2p_mining_fresh": 1, + "pool_backend_p2p_fresh_consensus_peer_count": 4, + "pool_backend_p2p_best_peer_lead_blocks": 2, + "pool_block_submit_accepted_total": 10, + "pool_block_submit_rejected_total": 3, + "pool_block_submit_rejected_by_reason": {"tip-overdue": 2, "stale-job": 1}, + "pool_blocks_found_total": 13, + "pool_shares_accepted_total": 100, + "pool_shares_rejected_total": 10, + "pool_share_reject_by_reason": {"invalidated_job": 7, "non_current_job": 3}, + "pool_template_invalidation_by_cause": {"parent-changed": 4}, + "pool_clean_refresh_by_event": {"broadcast": 8, "readiness_hold": 1}, + "pool_template_conversion_failure_ratio": 7.5, }, { "sampled_at": "2026-05-26T00:00:10+00:00", @@ -69,16 +199,43 @@ def test_summarize_samples_reports_block_rate_and_worker_ranges(self) -> None: "source": "fixture", "overall": "ok", "mode": "ready_no_miners", + "can_mine": True, + "can_submit_blocks": True, "sync_status": "synced", "current_block": 1020, + "current_block_source": "native", "remaining_blocks": 0, + "native_is_current": True, + "chain_syncing": True, + "mining_advisory_sync": True, + "p2p_connections": 8, + "p2p_network_gap": 0, "connected_miners": 0, "managed_miners": 0, "collection_ms": 20.0, + "status_age_seconds": 3.0, + "status_sampler_hit": False, "chain_rpc_latency_ms_max": 7.0, "iowait_percent": 2.0, "adaptive_workers": {"global_rpc": 3}, "host_profile": {"profile": "pi5", "os": "linux", "arch": "arm64"}, + "pool_active_connections": 2, + "pool_job_health_ready_miners": 1, + "pool_backend_mineable": 1, + "pool_backend_submit_ready": 0, + "pool_backend_p2p_mining_fresh": 1, + "pool_backend_p2p_fresh_consensus_peer_count": 3, + "pool_backend_p2p_best_peer_lead_blocks": 0, + "pool_block_submit_accepted_total": 18, + "pool_block_submit_rejected_total": 4, + "pool_block_submit_rejected_by_reason": {"tip-overdue": 3, "stale-job": 1}, + "pool_blocks_found_total": 22, + "pool_shares_accepted_total": 140, + "pool_shares_rejected_total": 17, + "pool_share_reject_by_reason": {"invalidated_job": 11, "non_current_job": 6}, + "pool_template_invalidation_by_cause": {"parent-changed": 9}, + "pool_clean_refresh_by_event": {"broadcast": 13, "readiness_hold": 2}, + "pool_template_conversion_failure_ratio": 9.5, }, ] @@ -86,9 +243,204 @@ def test_summarize_samples_reports_block_rate_and_worker_ranges(self) -> None: self.assertEqual(summary["sample_count"], 2) self.assertEqual(summary["block_delta"], 20) + self.assertTrue(summary["block_delta_valid"]) self.assertEqual(summary["blocks_per_second"], 2.0) + self.assertEqual(summary["current_block_source_first"], "native") + self.assertEqual(summary["current_block_source_last"], "native") + self.assertEqual(summary["can_submit_blocks_values"], ["false", "true"]) + self.assertEqual(summary["native_is_current_values"], ["false", "true"]) + self.assertEqual(summary["chain_syncing_values"], ["true"]) + self.assertEqual(summary["mining_advisory_sync_values"], ["false", "true"]) + self.assertEqual(summary["p2p_connections_min"], 6) + self.assertEqual(summary["p2p_network_gap_max"], 3) self.assertEqual(summary["chain_rpc_latency_ms_p95"], 7.0) self.assertEqual(summary["adaptive_worker_ranges"]["global_rpc"], {"min": 3, "max": 6}) + self.assertEqual(summary["pool_backend_mineable_min"], 1) + self.assertEqual(summary["pool_backend_submit_ready_min"], 0) + self.assertEqual(summary["pool_backend_peer_lead_max"], 2) + self.assertEqual(summary["pool_block_submit_accepted_delta"], 8) + self.assertEqual(summary["pool_block_submit_rejected_delta"], 1) + self.assertEqual(summary["pool_block_submit_rejected_per_accepted"], 0.125) + self.assertEqual(summary["pool_block_submit_rejected_by_reason_delta"], {"tip-overdue": 1}) + self.assertEqual(summary["pool_accepted_blocks_per_hour"], 2880.0) + self.assertEqual(summary["pool_blocks_found_delta"], 9) + self.assertEqual(summary["pool_shares_accepted_delta"], 40) + self.assertEqual(summary["pool_shares_rejected_delta"], 7) + self.assertEqual(summary["pool_share_reject_ratio"], 0.148936) + self.assertEqual(summary["pool_share_reject_by_reason_delta"], {"invalidated_job": 4, "non_current_job": 3}) + self.assertEqual(summary["pool_template_invalidation_by_cause_delta"], {"parent-changed": 5}) + self.assertEqual(summary["pool_clean_refresh_by_event_delta"], {"broadcast": 5, "readiness_hold": 1}) + self.assertEqual(summary["pool_ready_miners_min"], 1) + self.assertEqual(summary["pool_fresh_consensus_peers_min"], 3) + self.assertEqual(summary["pool_template_conversion_failure_ratio_max"], 9.5) + self.assertEqual(summary["status_age_seconds_p95"], 3.0) + self.assertEqual(summary["status_age_seconds_max"], 3.0) + self.assertEqual(summary["status_sampler_hit_values"], ["false", "true"]) + + def test_summarize_samples_treats_new_reject_counters_as_zero_at_window_start(self) -> None: + samples = [ + { + "sampled_at": "2026-05-26T00:00:00+00:00", + "sampled_epoch": 100, + "source": "fixture", + "current_block": 1000, + "current_block_source": "native", + "adaptive_workers": {}, + "pool_block_submit_accepted_total": 7, + "pool_block_submit_rejected_by_reason": {}, + "pool_shares_accepted_total": 20, + "pool_share_reject_by_reason": {}, + }, + { + "sampled_at": "2026-05-26T00:05:00+00:00", + "sampled_epoch": 400, + "source": "fixture", + "current_block": 1100, + "current_block_source": "native", + "adaptive_workers": {}, + "pool_block_submit_accepted_total": 150, + "pool_block_submit_rejected_total": 7, + "pool_block_submit_rejected_by_reason": { + "stale-job": 1, + "stale-parent": 4, + "tip-overdue": 2, + }, + "pool_shares_accepted_total": 120, + "pool_shares_rejected_total": 5, + "pool_share_reject_by_reason": {"stale_block_candidate": 5}, + }, + ] + + summary = measurement.summarize_samples(samples) + + self.assertEqual(summary["pool_block_submit_accepted_delta"], 143) + self.assertEqual(summary["pool_block_submit_rejected_delta"], 7) + self.assertEqual(summary["pool_block_submit_rejected_per_accepted"], 0.048951) + self.assertEqual( + summary["pool_block_submit_rejected_by_reason_delta"], + {"stale-job": 1, "stale-parent": 4, "tip-overdue": 2}, + ) + self.assertEqual(summary["pool_shares_rejected_delta"], 5) + self.assertEqual(summary["pool_share_reject_ratio"], 0.047619) + + def test_summarize_samples_keeps_reject_counter_unknown_when_first_metrics_scrape_failed(self) -> None: + samples = [ + { + "sampled_at": "2026-05-26T00:00:00+00:00", + "sampled_epoch": 100, + "source": "fixture", + "current_block": 1000, + "current_block_source": "native", + "adaptive_workers": {}, + "pool_metrics_error": "connection refused", + "pool_block_submit_accepted_total": 7, + }, + { + "sampled_at": "2026-05-26T00:05:00+00:00", + "sampled_epoch": 400, + "source": "fixture", + "current_block": 1100, + "current_block_source": "native", + "adaptive_workers": {}, + "pool_block_submit_accepted_total": 150, + "pool_block_submit_rejected_total": 7, + "pool_block_submit_rejected_by_reason": {"tip-overdue": 7}, + }, + ] + + summary = measurement.summarize_samples(samples) + + self.assertIsNone(summary["pool_block_submit_rejected_delta"]) + self.assertIsNone(summary["pool_block_submit_rejected_per_accepted"]) + self.assertEqual(summary["pool_block_submit_rejected_by_reason_delta"], {"tip-overdue": 7}) + + def test_collect_status_sample_limits_local_status_cache_age(self) -> None: + original_collect_status_cached = measurement.collect_status_cached + original_collect_pool_metrics_sample = measurement.collect_pool_metrics_sample + calls = {} + + def fake_collect_status_cached(*, include_logs, max_age_seconds): + calls["include_logs"] = include_logs + calls["max_age_seconds"] = max_age_seconds + return { + "overall": "ok", + "mode": "mining", + "can_mine": True, + "can_accept_shares": True, + "can_submit_blocks": True, + "sync_progress": {"status": "synced"}, + "adaptive_concurrency": {"workers": {}}, + } + + try: + measurement.collect_status_cached = fake_collect_status_cached + measurement.collect_pool_metrics_sample = lambda _url, _timeout: {} + sample = measurement.collect_status_sample( + status_url=None, + timeout=1.0, + pool_metrics_url=None, + local_status_max_age_seconds=2.5, + ) + finally: + measurement.collect_status_cached = original_collect_status_cached + measurement.collect_pool_metrics_sample = original_collect_pool_metrics_sample + + self.assertEqual(calls["include_logs"], False) + self.assertEqual(calls["max_age_seconds"], 2.5) + self.assertEqual(sample["source"], "local-collector") + self.assertTrue(sample["can_submit_blocks"]) + + def test_summarize_samples_marks_mixed_block_sources_untrusted(self) -> None: + samples = [ + { + "sampled_at": "2026-05-26T00:00:00+00:00", + "sampled_epoch": 100, + "source": "fixture", + "current_block": 2000, + "current_block_source": "eth_syncing", + "adaptive_workers": {}, + }, + { + "sampled_at": "2026-05-26T00:00:10+00:00", + "sampled_epoch": 110, + "source": "fixture", + "current_block": 1995, + "current_block_source": "native", + "adaptive_workers": {}, + }, + ] + + summary = measurement.summarize_samples(samples) + + self.assertIsNone(summary["block_delta"]) + self.assertFalse(summary["block_delta_valid"]) + self.assertEqual(summary["block_delta_warning"], "current_block source missing, changed, or moved backwards") + self.assertIsNone(summary["blocks_per_second"]) + + def test_summarize_samples_rejects_missing_block_sources(self) -> None: + samples = [ + { + "sampled_at": "2026-05-26T00:00:00+00:00", + "sampled_epoch": 100, + "source": "fixture", + "current_block": 2000, + "adaptive_workers": {}, + }, + { + "sampled_at": "2026-05-26T00:00:10+00:00", + "sampled_epoch": 110, + "source": "fixture", + "current_block": 2010, + "adaptive_workers": {}, + }, + ] + + summary = measurement.summarize_samples(samples) + + self.assertIsNone(summary["block_delta"]) + self.assertFalse(summary["block_delta_valid"]) + self.assertEqual(summary["block_delta_warning"], "current_block source missing, changed, or moved backwards") + self.assertIsNone(summary["blocks_per_second"]) if __name__ == "__main__": diff --git a/ops/tests/test_pipeline_profiler.py b/ops/tests/test_pipeline_profiler.py new file mode 100644 index 0000000..d94bb4e --- /dev/null +++ b/ops/tests/test_pipeline_profiler.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +import json +import pathlib +import sys +import unittest + +OPS_DIR = pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0, str(OPS_DIR)) + +import pipeline_profiler as profiler # noqa: E402 + + +class PipelineProfilerTests(unittest.TestCase): + def test_global_paid_rows_are_normalized_by_x100_count(self) -> None: + payload = { + "updated_at": "Jun 21, 2026 14:26:44 SAST", + "rows": [ + {"key": "0x1719e0ee598c15957448d5e568948101df78e7a0", "pool": "0x1719...e7a0", "blocks": "76", "local": "true"}, + {"key": "0xd5f8df0a60bc1ff4636250b2a2dff319bf79b1f5", "pool": "0xd5f8...b1f5", "blocks": "110"}, + {"key": "0x94390581d27ac0faf4792984068e9a4366e3ebe0", "pool": "0x9439...ebe0", "blocks": "51"}, + ], + } + + compact = profiler.compact_global_paid(payload, 1.2, None) + + rows = {row["pool"]: row for row in compact["tracked_rows"]} + self.assertEqual(rows["0x1719...e7a0"]["x100_count"], 4) + self.assertEqual(rows["0xd5f8...b1f5"]["x100_count"], 2) + self.assertEqual(rows["0x9439...ebe0"]["x100_count"], 1) + self.assertEqual(rows["0x1719...e7a0"]["blocks_per_x100"], 19) + self.assertEqual(rows["0xd5f8...b1f5"]["blocks_per_x100"], 55) + self.assertAlmostEqual(compact["local_vs_benchmarks"]["b1f5"]["local_vs_benchmark_rate"], 0.345455) + + def test_parse_psql_json_ignores_noise(self) -> None: + payload = {"accepted": 10, "job_age_ms": {"p95": 2500}} + + parsed = profiler.parse_psql_json("psql banner\n" + json.dumps(payload) + "\n") + + self.assertEqual(parsed, payload) + + def test_compact_metrics_extracts_timing_pipeline_fields(self) -> None: + metrics = """ +pool_job_health_ready_miners{pool_id="0"} 4 +pool_job_health_max_current_job_age_seconds{pool_id="0"} 0.296 +pool_block_submit_outcomes_total{outcome="accepted",pool_id="0",reason="ok"} 100 +pool_block_submit_outcomes_total{outcome="rejected",pool_id="0",reason="tip-overdue"} 7 +pool_rpc_backend_node_health_submit_ready{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_p2p_mining_fresh{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_template_invalidations_total{cause="state-changed",node="node",pool_id="0"} 11 +pool_template_fetch_duration_seconds_sum{pool_id="0"} 4 +pool_template_fetch_duration_seconds_count{pool_id="0"} 8 +pool_rpc_backend_submit_duration_seconds_sum{node="node",pool_id="0",result="accepted"} 2 +pool_rpc_backend_submit_duration_seconds_count{node="node",pool_id="0",result="accepted"} 4 +pool_block_candidate_reject_job_age_seconds_sum{node="node",pool_id="0",reason="stale-job"} 10 +pool_block_candidate_reject_job_age_seconds_count{node="node",pool_id="0",reason="stale-job"} 2 +""" + + compact = profiler.compact_metrics(metrics, 2.0, None) + + self.assertEqual(compact["ready_miners"], 4) + self.assertEqual(compact["current_job_age_seconds_max"], 0.296) + self.assertEqual(compact["submit_accepted_total"], 100) + self.assertEqual(compact["submit_rejected_by_reason"], {"tip-overdue": 7}) + self.assertEqual(compact["template_invalidations_by_cause"], {"state-changed": 11}) + self.assertEqual(compact["template_fetch_duration"]["total"]["avg_seconds"], 0.5) + self.assertEqual(compact["rpc_submit_duration_by_result"]["accepted"]["avg_seconds"], 0.5) + self.assertEqual(compact["candidate_reject_job_age_by_reason"]["stale-job"]["avg_seconds"], 5.0) + + def test_summary_reports_counter_deltas_and_job_age_risk(self) -> None: + samples = [ + { + "sampled_at": "a", + "sampled_epoch": 100, + "metrics": { + "submit_accepted_total": 10, + "submit_rejected_total": 2, + "submit_rejected_by_reason": {"tip-overdue": 2}, + "template_broadcasts_total": 20, + "template_invalidations_by_cause": {"state-changed": 1}, + "ready_miners": 4, + "backend_submit_ready": 1, + "backend_p2p_fresh": 1, + }, + "db_submission_timing": {"job_age_ms": {"p95": 1000}}, + }, + { + "sampled_at": "b", + "sampled_epoch": 160, + "metrics": { + "submit_accepted_total": 25, + "submit_rejected_total": 6, + "submit_rejected_by_reason": {"tip-overdue": 4, "stale-job": 2}, + "template_broadcasts_total": 45, + "template_invalidations_by_cause": {"state-changed": 5}, + "ready_miners": 0, + "backend_submit_ready": 0, + "backend_p2p_fresh": 1, + }, + "db_submission_timing": {"job_age_ms": {"p95": 38000}}, + }, + ] + + summary = profiler.summarize_samples(samples) + + self.assertEqual(summary["accepted_submit_delta"], 15) + self.assertEqual(summary["rejected_submit_delta"], 4) + self.assertEqual(summary["submit_rejected_by_reason_delta"], {"stale-job": 2, "tip-overdue": 2}) + self.assertEqual(summary["template_broadcast_delta"], 25) + self.assertEqual(summary["template_invalidations_by_cause_delta"], {"state-changed": 4}) + self.assertEqual(summary["ready_miners_min"], 0) + self.assertEqual(summary["backend_submit_ready_min"], 0) + self.assertEqual(summary["db_job_age_p95_ms_max"], 38000) + + +if __name__ == "__main__": + unittest.main() diff --git a/ops/tests/test_pool_efficiency_loss_ledger.py b/ops/tests/test_pool_efficiency_loss_ledger.py index 4d61311..79e685f 100644 --- a/ops/tests/test_pool_efficiency_loss_ledger.py +++ b/ops/tests/test_pool_efficiency_loss_ledger.py @@ -51,11 +51,16 @@ def test_readiness_contract_distinguishes_contradiction_from_hard_unready(self) source_health = {"node_mineable": False, "node_submit_ready": False, "node_p2p_mining_fresh": True} job_health = {"ok": False} - contradiction = pool_ops.selected_backend_readiness_contract("node", source_health, job_health, True) - hard_unready = pool_ops.selected_backend_readiness_contract("node", source_health, job_health, False) + contradiction = pool_ops.selected_backend_readiness_contract("node", source_health, job_health, True, True) + paid_but_not_safe = pool_ops.selected_backend_readiness_contract("node", source_health, job_health, True, False) + hard_unready = pool_ops.selected_backend_readiness_contract("node", source_health, job_health, False, False) self.assertTrue(contradiction["contradiction"]) self.assertFalse(contradiction["hard_unready"]) + self.assertTrue(contradiction["readiness_override_safe"]) + self.assertTrue(paid_but_not_safe["contradiction"]) + self.assertTrue(paid_but_not_safe["hard_unready"]) + self.assertFalse(paid_but_not_safe["readiness_override_safe"]) self.assertFalse(hard_unready["contradiction"]) self.assertTrue(hard_unready["hard_unready"]) @@ -79,6 +84,101 @@ def test_selected_backend_unready_reasons_include_peer_freshness(self) -> None: ], ) + def test_native_p2p_current_safety_survives_template_readiness_flicker(self) -> None: + self.assertTrue( + pool_ops.selected_backend_native_p2p_current_safe( + { + "healthy": True, + "node_mineable": False, + "node_submit_ready": False, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 2, + "node_p2p_best_peer_lead_blocks": pool_ops.CATCHUP_NATIVE_P2P_MAX_PEER_LEAD_BLOCKS, + } + ) + ) + + def test_native_p2p_current_safety_rejects_peer_loss_and_peer_lead(self) -> None: + peer_loss = { + "healthy": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 1, + "node_p2p_best_peer_lead_blocks": 0, + } + peer_lead = { + "healthy": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": pool_ops.CATCHUP_NATIVE_P2P_MAX_PEER_LEAD_BLOCKS + 1, + } + stale = { + "healthy": True, + "node_p2p_mining_fresh": False, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, + } + + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(peer_loss)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(peer_lead)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(stale)) + + def test_selected_backend_safety_rejects_invalid_template_coinbase(self) -> None: + source_health = { + "healthy": True, + "node_template_coinbase_valid": False, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, + } + + self.assertFalse(pool_ops.selected_backend_mining_safe(source_health)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(source_health)) + self.assertIn("template_coinbase_valid=false", pool_ops.selected_backend_unready_reasons(source_health)) + + def test_template_coinbase_valid_signal_keeps_missing_template_unknown(self) -> None: + self.assertIsNone(pool_ops.template_coinbase_valid_signal(False, "", "node_syncing")) + self.assertFalse( + pool_ops.template_coinbase_valid_signal( + False, + "0x0000000000000000000000000000000000000000", + "zero-template-coinbase", + ) + ) + + def test_selected_backend_safety_fails_closed_on_unknown_peer_floor_or_lead(self) -> None: + base = { + "healthy": True, + "node_mineable": True, + "node_submit_ready": True, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 3, + "node_p2p_best_peer_lead_blocks": 0, + } + + self.assertTrue(pool_ops.selected_backend_mining_safe(base)) + self.assertTrue(pool_ops.selected_backend_native_p2p_current_safe(base)) + + missing_peers = dict(base) + missing_peers.pop("node_p2p_fresh_consensus_peer_count") + self.assertFalse(pool_ops.selected_backend_mining_safe(missing_peers)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(missing_peers)) + + missing_lead = dict(base) + missing_lead.pop("node_p2p_best_peer_lead_blocks") + self.assertFalse(pool_ops.selected_backend_mining_safe(missing_lead)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(missing_lead)) + + for lead in (10, 11, 12): + safe = dict(base, node_p2p_best_peer_lead_blocks=lead) + self.assertTrue(pool_ops.selected_backend_mining_safe(safe), f"lead={lead}") + self.assertTrue(pool_ops.selected_backend_native_p2p_current_safe(safe), f"lead={lead}") + + unsafe = dict(base, node_p2p_best_peer_lead_blocks=13) + self.assertFalse(pool_ops.selected_backend_mining_safe(unsafe)) + self.assertFalse(pool_ops.selected_backend_native_p2p_current_safe(unsafe)) + def test_selected_backend_source_degradation_is_advisory_with_recent_paid_work(self) -> None: advisory = pool_ops.selected_backend_source_degradation(True, True) hard = pool_ops.selected_backend_source_degradation(True, False) @@ -104,6 +204,51 @@ def test_catchup_policy_pauses_pool_above_threshold(self) -> None: self.assertIn("Leave miners configured", policy["user_message"]) self.assertEqual(policy["trigger"], "lag_threshold") + def test_catchup_policy_ignores_remaining_blocks_when_paid_work_recent(self) -> None: + policy = pool_ops.build_catchup_policy( + { + "status": "syncing", + "remaining_blocks": 14_982, + "nodes": {"node": {"remaining_blocks": 14_982, "peer_ahead_blocks": 24}}, + }, + {"node": {"remaining_blocks": 14_982, "peer_ahead_blocks": 24}}, + {"pool": {"running": True}}, + {}, + mining_ready=True, + ignore_remaining_blocks=True, + ) + + self.assertFalse(policy["active"]) + self.assertTrue(policy["mining_ready"]) + self.assertTrue(policy["remaining_blocks_advisory"]) + self.assertEqual(policy["lag_blocks"], 24) + + def test_catchup_policy_treats_evm_lag_as_advisory_when_native_p2p_is_safe(self) -> None: + source_health = { + "node_mineable": False, + "node_submit_ready": False, + "node_p2p_mining_fresh": True, + "node_p2p_fresh_consensus_peer_count": 7, + "node_p2p_best_peer_lead_blocks": 0, + } + policy = pool_ops.build_catchup_policy( + { + "status": "syncing", + "remaining_blocks": 14_982, + "nodes": {"node": {"remaining_blocks": 14_982}}, + }, + {"node": {"remaining_blocks": 14_982}}, + {"pool": {"running": True}}, + source_health, + mining_ready=pool_ops.selected_backend_native_p2p_current_safe(source_health), + ignore_remaining_blocks=pool_ops.selected_backend_native_p2p_current_safe(source_health), + ) + + self.assertFalse(policy["active"]) + self.assertEqual(policy["lag_blocks"], 0) + self.assertTrue(policy["mining_ready"]) + self.assertTrue(policy["remaining_blocks_advisory"]) + def test_catchup_policy_uses_io_pressure_as_primary_trigger(self) -> None: policy = pool_ops.build_catchup_policy( {"status": "syncing", "remaining_blocks": 80}, @@ -208,6 +353,53 @@ def test_pool_metrics_parse_loss_ledger_and_source_health_contract_inputs(self) self.assertEqual(payload["loss_ledger"]["severity"], "critical") self.assertEqual(payload["loss_ledger"]["share_outcomes"]["accepted_ratio_percent"], 25.0) + def test_pool_metrics_accept_node_label_for_backend_health(self) -> None: + metrics = """ +pool_active_connections 3 +pool_rpc_backend_selected{node="node",pool_id="0"} 1 +pool_rpc_backend_healthy{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_mineable{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_submit_ready{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_p2p_mining_fresh{node="node",pool_id="0"} 1 +pool_rpc_backend_node_health_p2p_fresh_consensus_peer_count{node="node",pool_id="0"} 3 +pool_rpc_backend_node_health_p2p_best_peer_lead_blocks{node="node",pool_id="0"} 0 +pool_job_health_ok{pool_id="0"} 1 +pool_job_health_ready_miners{pool_id="0"} 3 +pool_block_submit_outcomes_total{outcome="accepted",pool_id="0",reason="ok"} 4 +""" + pool_ops.fetch_text_url = lambda *_args, **_kwargs: metrics + + payload = pool_ops.collect_pool_prometheus_metrics( + {"asic-pool": {"running": True, "network_ips": ["10.0.0.2"]}} + ) + + self.assertEqual(payload["selected_backend"], "node") + self.assertTrue(payload["selected_backend_source_health"]["healthy"]) + self.assertTrue(payload["selected_backend_source_health"]["node_mineable"]) + self.assertTrue(pool_ops.selected_backend_mining_safe(payload["selected_backend_source_health"])) + + def test_pool_metrics_parse_template_coinbase_rejects_and_local_stale_ratio(self) -> None: + metrics = """ +pool_active_connections 4 +pool_template_coinbase_rejects_total{pool_id="0",reason="zero-template-address",source="ws-push"} 7 +pool_template_coinbase_rejects_total{pool_id="0",reason="wrong-template-address",source="http-fetch"} 2 +pool_block_submit_outcomes_total{outcome="accepted",pool_id="0",reason="ok"} 10 +pool_block_submit_outcomes_total{outcome="rejected-local",pool_id="0",reason="stale-job"} 3 +pool_block_submit_outcomes_total{outcome="rejected-local",pool_id="0",reason="stale-parent"} 2 +""" + pool_ops.fetch_text_url = lambda *_args, **_kwargs: metrics + + payload = pool_ops.collect_pool_prometheus_metrics( + {"asic-pool": {"running": True, "network_ips": ["10.0.0.2"]}} + ) + stale_stats = pool_ops.local_stale_block_submit_stats(payload["block_submit_outcomes"]) + + self.assertEqual(payload["template_coinbase_rejects"]["ws-push:zero-template-address"], 7) + self.assertEqual(pool_ops.zero_coinbase_reject_total(payload["template_coinbase_rejects"]), 7) + self.assertEqual(stale_stats["accepted"], 10) + self.assertEqual(stale_stats["local_stale"], 5) + self.assertEqual(stale_stats["local_stale_ratio"], 0.333333) + if __name__ == "__main__": unittest.main() diff --git a/ops/tests/test_savepoint_manifest.py b/ops/tests/test_savepoint_manifest.py new file mode 100644 index 0000000..11e3db6 --- /dev/null +++ b/ops/tests/test_savepoint_manifest.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import json +import pathlib +import re +import unittest + + +ROOT_DIR = pathlib.Path(__file__).resolve().parents[2] +SAVEPOINT = ROOT_DIR / "ops/savepoints/20260621-202754-good-mining-state.json" +HEX40 = re.compile(r"^[0-9a-f]{40}$") +SHA256 = re.compile(r"^sha256:[0-9a-f]{64}$") + + +class SavepointManifestTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.manifest = json.loads(SAVEPOINT.read_text(encoding="utf-8")) + + def test_manifest_pins_source_and_runtime_state(self) -> None: + self.assertEqual(self.manifest["schema_version"], 1) + self.assertEqual( + self.manifest["savepoint_id"], + "20260621-202754-stack-redis-good-mining-state", + ) + + expected_repos = {"stack-redis", "pool", "blockdag-corechain", "redis-dash"} + self.assertEqual(set(self.manifest["source_commits"]), expected_repos) + for repo, state in self.manifest["source_commits"].items(): + self.assertTrue(state["branch"], repo) + self.assertRegex(state["commit"], HEX40, repo) + + expected_images = { + "stack-node", + "stack-pool", + "stack-dashboard", + "stack-status-sampler", + "stack-watchdog", + "stack-sentinel", + } + self.assertEqual(set(self.manifest["runtime_images"]), expected_images) + for image, digest in self.manifest["runtime_images"].items(): + self.assertRegex(digest, SHA256, image) + + def test_non_regression_contract_keeps_mining_safety_gates(self) -> None: + contract = self.manifest["non_regression_contract"] + gates = contract["minimum_live_gates"] + + self.assertIs(gates["dashboard_can_accept_shares"], True) + self.assertIs(gates["dashboard_can_submit_blocks"], True) + self.assertGreaterEqual(gates["node_p2p_fresh_consensus_peer_count"], 2) + self.assertEqual(gates["pool_active_connections"], 4) + self.assertEqual(gates["pool_ready_miners"], 4) + self.assertEqual(gates["asic_ready_lanes"], 4) + + preserve_text = "\n".join(contract["must_preserve"]) + self.assertIn("coinbase", preserve_text) + self.assertIn("before miner broadcast", preserve_text) + self.assertIn("ASIC performance rows", preserve_text) + + def test_live_evidence_represents_good_four_asic_state(self) -> None: + evidence = self.manifest["live_evidence"] + dashboard = evidence["dashboard_status"] + node = evidence["node_template_health"] + pool = evidence["pool_metrics"] + asics = evidence["asic_performance"] + + self.assertEqual(dashboard["mode"], "mining") + self.assertIs(dashboard["can_accept_shares"], True) + self.assertIs(dashboard["can_submit_blocks"], True) + self.assertEqual(dashboard["miner_health"], {"managed": 4, "connected": 4, "configured": 4}) + + self.assertIs(node["mineable_now"], True) + self.assertIs(node["submit_ready"], True) + self.assertIs(node["get_block_template_ready"], True) + self.assertEqual(node["sync_reason_code"], "ok") + self.assertIs(node["p2p_mining_fresh"], True) + self.assertGreaterEqual(node["p2p_fresh_consensus_peer_count"], 2) + self.assertIs(node["chain_current"], True) + + self.assertEqual(pool["active_connections"], 4) + self.assertEqual(pool["ready_miners"], 4) + self.assertGreater(pool["block_submit_outcomes"]["accepted_ok"], 0) + + self.assertEqual(asics["lanes"], 4) + self.assertEqual(asics["ready"], 4) + self.assertEqual(asics["accepted_30m"], 566) + self.assertEqual( + sum(lane["blocks_30m"] for lane in asics["lanes_detail"]), + asics["accepted_30m"], + ) + + def test_asic_identity_baseline_is_explicit(self) -> None: + lanes = self.manifest["live_evidence"]["asic_performance"]["lanes_detail"] + by_mac = {lane["asic_mac"]: lane for lane in lanes} + self.assertEqual( + set(by_mac), + { + "28:e2:97:1e:c0:b5", + "28:e2:97:3e:39:63", + "28:e2:97:4d:44:3a", + "2a:71:c7:f5:1f:1e", + }, + ) + self.assertEqual(by_mac["28:e2:97:1e:c0:b5"]["remote_host"], "192.168.1.101") + self.assertEqual(by_mac["28:e2:97:3e:39:63"]["remote_host"], "192.168.1.14") + self.assertEqual(by_mac["28:e2:97:4d:44:3a"]["remote_host"], "192.168.1.105") + self.assertEqual(by_mac["2a:71:c7:f5:1f:1e"]["remote_host"], "192.168.1.102") + for lane in by_mac.values(): + self.assertIs(lane["ready"], True) + self.assertEqual(lane["health"], "ok") + self.assertGreater(lane["blocks_30m"], 0) + + def test_zero_coinbase_follow_up_does_not_weaken_fail_closed_rule(self) -> None: + follow_up = self.manifest["known_follow_up"] + rejects = self.manifest["live_evidence"]["pool_metrics"]["template_coinbase_rejects"] + + self.assertGreater(rejects["ws_push_zero_template_address"], 0) + self.assertIn("zero-template-address", follow_up["issue"]) + self.assertIn("fix upstream", follow_up["policy"]) + self.assertIn("Do not weaken", follow_up["policy"]) + self.assertIn("zero-coinbase templates", follow_up["policy"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/ops/tests/test_single_gate_orchestration.py b/ops/tests/test_single_gate_orchestration.py index 362a194..8b99761 100644 --- a/ops/tests/test_single_gate_orchestration.py +++ b/ops/tests/test_single_gate_orchestration.py @@ -31,7 +31,24 @@ def unsafe_catchup_status() -> dict[str, object]: } -def safe_canonical_status() -> dict[str, object]: +def native_health(**overrides: object) -> dict[str, object]: + health: dict[str, object] = { + "chain_current": True, + "p2p_mining_fresh": True, + "p2p_mining_fresh_reason_code": "ok", + "p2p_fresh_consensus_peer_count": 2, + "p2p_best_peer_lead_blocks": 0, + "get_block_template_ready": True, + "submit_ready": True, + "mineable_now": True, + "template_usable": True, + "sync_allowed": True, + } + health.update(overrides) + return health + + +def safe_native_status() -> dict[str, object]: return { "fresh": True, "mode": "synced", @@ -42,6 +59,7 @@ def safe_canonical_status() -> dict[str, object]: "peer_count": 2, "nodes": { "blockdag-node-1": { + "native_template_health": native_health(), "canonical_mining_safety": { "safe": True, "schema": "stack_evm_public_reference_v1", @@ -54,6 +72,35 @@ def safe_canonical_status() -> dict[str, object]: } +def advisory_evm_sync_status() -> dict[str, object]: + status = safe_native_status() + status["mode"] = "catchup_pause" + status["overall"] = "syncing" + status["status_reason"] = "catch-up pause active: EVM reference is behind, native mining safety is current" + status["catchup_policy"] = { + "active": True, + "remaining_blocks_advisory": True, + "native_advisory_safe": True, + } + status["sync_health"] = { + "catchup_pause_active": True, + "native_chain_progress_safe": True, + } + sync_progress = status["sync_progress"] + sync_progress["status"] = "syncing" + sync_progress["remaining_blocks"] = 5 + sync_progress["chain_syncing"] = False + sync_progress["evm_chain_syncing"] = True + sync_progress["mining_advisory_sync"] = True + node = sync_progress["nodes"]["blockdag-node-1"] + node["status"] = "syncing" + node["remaining_blocks"] = 5 + node["chain_syncing"] = False + node["evm_chain_syncing"] = True + node["mining_advisory_sync"] = True + return status + + class SingleGateOrchestrationTests(unittest.TestCase): def setUp(self) -> None: self.original_stack_services = list(pool_ops.STACK_SERVICES) @@ -80,7 +127,7 @@ def test_shared_gate_blocks_catchup_status(self) -> None: self.assertIn("overall stack status is syncing", decision.reason) def test_shared_gate_blocks_sync_progress_even_when_top_level_is_mining(self) -> None: - status = safe_canonical_status() + status = safe_native_status() status["mode"] = "mining" status["overall"] = "ok" sync_progress = status["sync_progress"] @@ -92,7 +139,48 @@ def test_shared_gate_blocks_sync_progress_even_when_top_level_is_mining(self) -> self.assertFalse(decision.allowed) self.assertIn("sync progress is syncing with 5 block(s) remaining", decision.reason) - def test_shared_gate_blocks_synced_status_without_canonical_proof(self) -> None: + def test_shared_gate_allows_native_safe_advisory_evm_sync(self) -> None: + decision = pool_start_gate.pool_start_decision(advisory_evm_sync_status()) + + self.assertTrue(decision.allowed, decision.reason) + + def test_shared_gate_blocks_advisory_evm_sync_when_native_proof_is_unsafe(self) -> None: + status = advisory_evm_sync_status() + node = status["sync_progress"]["nodes"]["blockdag-node-1"] + node["native_template_health"] = native_health( + p2p_mining_fresh=False, + p2p_mining_fresh_reason_code="no_fresh_peers", + ) + + decision = pool_start_gate.pool_start_decision(status) + + self.assertFalse(decision.allowed) + self.assertIn("p2p_mining_fresh_not_true:no_fresh_peers", decision.reason) + self.assertIn("chain catch-up pause is active", decision.reason) + + def test_shared_gate_peer_lead_threshold_is_inclusive_and_fails_closed_when_unknown(self) -> None: + for lead in (10, 11, 12): + status = safe_native_status() + node = status["sync_progress"]["nodes"]["blockdag-node-1"] + node["native_template_health"] = native_health(p2p_best_peer_lead_blocks=lead) + decision = pool_start_gate.pool_start_decision(status) + self.assertTrue(decision.allowed, f"lead={lead}: {decision.reason}") + + unsafe = safe_native_status() + unsafe["sync_progress"]["nodes"]["blockdag-node-1"]["native_template_health"] = native_health( + p2p_best_peer_lead_blocks=13 + ) + self.assertFalse(pool_start_gate.pool_start_decision(unsafe).allowed) + + unknown = safe_native_status() + unknown["sync_progress"]["nodes"]["blockdag-node-1"]["native_template_health"].pop( + "p2p_best_peer_lead_blocks" + ) + decision = pool_start_gate.pool_start_decision(unknown) + self.assertFalse(decision.allowed) + self.assertIn("p2p_peer_lead_unknown", decision.reason) + + def test_shared_gate_blocks_synced_status_without_native_proof(self) -> None: decision = pool_start_gate.pool_start_decision( { "fresh": True, @@ -104,15 +192,44 @@ def test_shared_gate_blocks_synced_status_without_canonical_proof(self) -> None: ) self.assertFalse(decision.allowed) - self.assertIn("canonical public-chain safety proof is missing", decision.reason) + self.assertIn("native mining safety proof is missing", decision.reason) + + def test_shared_gate_blocks_canonical_only_status_without_native_proof(self) -> None: + status = safe_native_status() + node = status["sync_progress"]["nodes"]["blockdag-node-1"] + node.pop("native_template_health") + + decision = pool_start_gate.pool_start_decision(status) + + self.assertFalse(decision.allowed) + self.assertIn("native mining safety proof is missing", decision.reason) + + def test_shared_gate_allows_ready_status_with_native_proof(self) -> None: + decision = pool_start_gate.pool_start_decision(safe_native_status()) - def test_shared_gate_allows_ready_status_with_canonical_proof(self) -> None: - decision = pool_start_gate.pool_start_decision(safe_canonical_status()) + self.assertTrue(decision.allowed, decision.reason) + + def test_shared_gate_allows_native_safe_status_without_public_canonical_proof(self) -> None: + status = safe_native_status() + node = status["sync_progress"]["nodes"]["blockdag-node-1"] + node.pop("canonical_mining_safety") + + decision = pool_start_gate.pool_start_decision(status) self.assertTrue(decision.allowed, decision.reason) + def test_shared_gate_blocks_native_unsafe_even_with_public_canonical_proof(self) -> None: + status = safe_native_status() + node = status["sync_progress"]["nodes"]["blockdag-node-1"] + node["native_template_health"] = native_health(p2p_mining_fresh=False, p2p_mining_fresh_reason_code="no_fresh_peers") + + decision = pool_start_gate.pool_start_decision(status) + + self.assertFalse(decision.allowed) + self.assertIn("p2p_mining_fresh_not_true:no_fresh_peers", decision.reason) + def test_shared_gate_blocks_single_peer_even_when_synced(self) -> None: - status = safe_canonical_status() + status = safe_native_status() status["sync_progress"]["peer_count"] = 1 decision = pool_start_gate.pool_start_decision(status) diff --git a/ops/tests/test_stack_defaults.py b/ops/tests/test_stack_defaults.py index 75d02c6..f509a6c 100644 --- a/ops/tests/test_stack_defaults.py +++ b/ops/tests/test_stack_defaults.py @@ -37,6 +37,32 @@ def test_compose_tip_lag_fallback_matches_stack_default(self) -> None: ) self.assertIn(expected, compose) + def test_stale_race_reconnect_is_disabled_by_default(self) -> None: + defaults = parse_env(ROOT_DIR / "ops/config/stack-defaults.env") + compose = (ROOT_DIR / "docker-compose.yml").read_text(encoding="utf-8") + + self.assertEqual(defaults["POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD"], "0") + self.assertIn( + "POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD: " + "${POOL_STALE_RACE_CLIENT_RECONNECT_THRESHOLD:-0}", + compose, + ) + + def test_evm_head_guard_is_advisory_by_default(self) -> None: + defaults = parse_env(ROOT_DIR / "ops/config/stack-defaults.env") + env_example = parse_env(ROOT_DIR / ".env.example") + portable = parse_env(ROOT_DIR / "ops/portable.env.example") + compose = (ROOT_DIR / "docker-compose.yml").read_text(encoding="utf-8") + + self.assertEqual(defaults["POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED"], "false") + self.assertEqual(env_example["POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED"], "false") + self.assertEqual(portable["POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED"], "false") + self.assertIn( + "POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED: " + "${POOL_RPC_ROUTER_EVM_HEAD_GUARD_ENABLED:-false}", + compose, + ) + def test_pool_database_defaults_match_compose(self) -> None: defaults = parse_env(ROOT_DIR / "ops/config/stack-defaults.env") env_example = parse_env(ROOT_DIR / ".env.example") @@ -49,6 +75,44 @@ def test_pool_database_defaults_match_compose(self) -> None: self.assertIn('os.environ.get("BDAG_POOL_DB_USER", "bdag_pool")', pool_ops) self.assertIn('os.environ.get("BDAG_POOL_DB_NAME", "bdagpool")', pool_ops) + def test_native_safe_mining_defaults_are_stack_owned(self) -> None: + defaults = parse_env(ROOT_DIR / "ops/config/stack-defaults.env") + env_example = parse_env(ROOT_DIR / ".env.example") + compose = (ROOT_DIR / "docker-compose.yml").read_text(encoding="utf-8") + + expected = { + "NODE_DATA_DIR": "./data/node", + "POOL_ASIC_ARP_TABLE_PATH": "/host/proc/net/arp", + "POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS": "15", + "POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS": "2", + "POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS": "1750", + "POOL_TEMPLATE_TTL_REFRESH_MS": "100", + "POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS": "1750", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_JOB_AGE_MS": "1750", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_JOB_AGE_MS": "1750", + "POOL_BLOCK_TIMING_CONTROLLER_MIN_TEMPLATE_TTL_MS": "100", + "POOL_BLOCK_TIMING_CONTROLLER_MAX_TEMPLATE_TTL_MS": "100", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE": "true", + "POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS": "8000", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED": "false", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS": "0", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS": "20", + "POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS": "1000", + "BDAG_ENABLE_NODE_MINING": "0", + "BDAG_NODE_MODULES": "Blockdag,miner", + "BDAG_EVM_SYNC_BACKOFF_SECONDS": "60", + } + for key, value in expected.items(): + self.assertEqual(defaults[key], value) + self.assertEqual(env_example[key], value) + self.assertIn(f"${{{key}:-{value}}}", compose) + + self.assertIn( + "MINING_POOL_ADDRESS: ${MINING_POOL_ADDRESS:?set MINING_POOL_ADDRESS to a non-zero payout address}", + compose, + ) + self.assertIn("POOL_COINBASE_ADDRESS: ${POOL_COINBASE_ADDRESS:-}", compose) + def test_stack_defaults_validator_passes(self) -> None: result = subprocess.run( ["python3", "scripts/validate-stack-defaults.py"], diff --git a/ops/tests/test_stack_status_source.py b/ops/tests/test_stack_status_source.py new file mode 100644 index 0000000..513af5f --- /dev/null +++ b/ops/tests/test_stack_status_source.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import pathlib +import sys +import unittest +from unittest import mock + +OPS_DIR = pathlib.Path(__file__).resolve().parents[1] +sys.path.insert(0, str(OPS_DIR)) + +import stack_status_source # noqa: E402 + + +class StackStatusSourceTests(unittest.TestCase): + def test_compact_http_status_falls_back_to_in_process_repair_status(self) -> None: + compact_dashboard_payload = { + "status": "degraded", + "mode": "node_syncing", + "sync_progress": {"status": "p2p_down"}, + } + in_process_payload = { + "failures": [], + "warnings": ["native P2P is down"], + "overall": "degraded", + } + + with mock.patch.object(stack_status_source, "_fixture_payload", return_value=None), mock.patch.object( + stack_status_source, "_env_urls", return_value=["http://dashboard:8088/api/status"] + ), mock.patch.object( + stack_status_source, "fetch_http_status", return_value=compact_dashboard_payload + ), mock.patch.object( + stack_status_source, "collect_status_cached", return_value=in_process_payload + ): + status = stack_status_source.collect_stack_status() + + self.assertEqual("in-process", status["stack_status_source"]["source"]) + self.assertEqual("degraded", status["overall"]) + self.assertIn("missing repair schema key", status["stack_status_source"]["errors"][0]) + + def test_repair_complete_http_status_is_used_directly(self) -> None: + http_payload = { + "failures": [], + "warnings": [], + "overall": "ok", + } + + with mock.patch.object(stack_status_source, "_fixture_payload", return_value=None), mock.patch.object( + stack_status_source, "_env_urls", return_value=["http://dashboard:8088/api/status"] + ), mock.patch.object( + stack_status_source, "fetch_http_status", return_value=http_payload + ), mock.patch.object( + stack_status_source, + "collect_status_cached", + side_effect=AssertionError("complete HTTP status should not fall back"), + ): + status = stack_status_source.collect_stack_status() + + self.assertEqual("status-http", status["stack_status_source"]["source"]) + self.assertEqual("ok", status["overall"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/ops/tests/test_status_sampler_mining_imperative.py b/ops/tests/test_status_sampler_mining_imperative.py index ff01257..f2a9aa8 100644 --- a/ops/tests/test_status_sampler_mining_imperative.py +++ b/ops/tests/test_status_sampler_mining_imperative.py @@ -47,6 +47,7 @@ def setUp(self) -> None: "CHAIN_STATE_STALLED_IMPORT_RESTORE_SECONDS", "CHAIN_STATE_STALLED_IMPORT_RESTORE_PEER_AHEAD_BLOCKS", "CHAIN_STATE_STALLED_IMPORT_RESTORE_GAP_GROWTH_BLOCKS", + "CHAIN_STATE_IMPORT_WATCH_FILE", "EVM_REFERENCE_GAP_STALL_RESTORE_ENABLED", "EVM_REFERENCE_GAP_STALL_RESTORE_SECONDS", "EVM_REFERENCE_GAP_STALL_MIN_LAG_BLOCKS", @@ -132,6 +133,23 @@ def restore(self) -> None: def command_result(self, command: list[str], returncode: int = 0, stdout: str = "", stderr: str = ""): return pool_ops.CommandResult(command, returncode, stdout, stderr, 0.0) + def test_set_env_file_value_quotes_values_with_spaces(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + env_path = pathlib.Path(tmp) / ".env" + env_path.write_text("NODE_ARGS_APPEND=\n", encoding="utf-8") + + changed = status_sampler.set_env_file_value( + env_path, + "NODE_ARGS_APPEND", + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc", + ) + + self.assertTrue(changed) + self.assertIn( + 'NODE_ARGS_APPEND="--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc"', + env_path.read_text(encoding="utf-8"), + ) + def pool_compose_start_seen(self, commands: list[list[str]]) -> bool: return any( "compose" in command @@ -151,6 +169,20 @@ def canonical_safety(self, safe: bool = True) -> dict: "reason": "external public-chain proof matches local node" if safe else "public-chain proof failed", } + def native_template_health(self, safe: bool = True) -> dict: + return { + "chain_current": safe, + "p2p_mining_fresh": safe, + "p2p_mining_fresh_reason_code": "ok" if safe else "no_fresh_peers", + "p2p_fresh_consensus_peer_count": 2 if safe else 0, + "p2p_best_peer_lead_blocks": 0, + "get_block_template_ready": safe, + "submit_ready": safe, + "mineable_now": safe, + "template_usable": safe, + "sync_allowed": safe, + } + def stopped_pool_payload(self, sync_status: str = "syncing", remaining_blocks: int = 5) -> dict: payload = { "overall": "syncing" if sync_status != "synced" else "ok", @@ -168,7 +200,10 @@ def stopped_pool_payload(self, sync_status: str = "syncing", remaining_blocks: i } if sync_status == "synced": payload["sync_progress"]["nodes"] = { - "blockdag-node-1": {"canonical_mining_safety": self.canonical_safety(True)} + "blockdag-node-1": { + "canonical_mining_safety": self.canonical_safety(True), + "native_template_health": self.native_template_health(True), + } } return payload @@ -197,6 +232,36 @@ def evm_gap_payload(self, *, lag: int = 14_100, local: int = 11_690_000) -> dict "pool_metrics": {"active_connections": 0, "source_job_health": {}}, } + def native_peer_lag_payload(self, *, height: int = 12_115_316, peer_lag: int = 1_200) -> dict: + return { + "overall": "syncing", + "sync_warnings": ["native peers are ahead"], + "containers": {status_sampler.POOL_CONTAINER: {"running": False}}, + "sync_progress": { + "status": "syncing", + "chain_block_count": height, + "current_block": height, + "peer_ahead_blocks": peer_lag, + "remaining_blocks": peer_lag, + "peer_count": 2, + "p2p_connections": 2, + "nodes": { + "node": { + "status": "syncing", + "current_block": height, + "peer_ahead_blocks": peer_lag, + "peer_count": 2, + "p2p_connections": 2, + } + }, + }, + "sync_health": {}, + "nodes": {"node": {"latest_block": height, "peer_ahead_blocks": peer_lag}}, + "miner_health": {"connected_count": 0, "managed_count": 0}, + "pool": {"metrics": {"active_connections": 0}, "source_job_health": {}}, + "pool_metrics": {"active_connections": 0, "source_job_health": {}}, + } + def test_evm_reference_gap_stall_requires_restore_when_gap_does_not_close(self) -> None: now = 1_779_200_000 with tempfile.TemporaryDirectory() as tmpdir: @@ -221,6 +286,172 @@ def test_evm_reference_gap_stall_requires_restore_when_gap_does_not_close(self) self.assertIn("EVM reference gap has not improved", decision["reasons"][0]) self.assertTrue(decision["evm_reference_gap"]["restore_required"]) + def test_evm_reference_gap_stall_is_advisory_when_native_paid_mining_is_safe(self) -> None: + now = 1_779_200_000 + payload = self.evm_gap_payload(lag=14_140, local=11_691_000) + payload["sync_health"] = {"pool_has_recent_paid_work": True} + payload["sync_progress"]["nodes"] = { + "node": {"native_template_health": self.native_template_health(True)} + } + with tempfile.TemporaryDirectory() as tmpdir: + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE = pathlib.Path(tmpdir) / "evm-gap-watch.json" + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE.write_text( + json.dumps( + { + "candidate": True, + "best_lag_blocks": 14_100, + "first_unimproved_epoch": now - 901, + } + ), + encoding="utf-8", + ) + with mock.patch.object(status_sampler.time, "time", return_value=now): + decision = status_sampler.chain_state_restore_decision(payload) + + self.assertFalse(decision["should_repair"]) + self.assertFalse(decision["evm_reference_gap"]["restore_required"]) + self.assertTrue(decision["evm_reference_gap"]["would_restore_required"]) + self.assertTrue(decision["evm_reference_gap"]["restore_suppressed_by_native_paid_work"]) + self.assertIn("native mining safety", decision["evm_reference_gap"]["reason"]) + + def test_evm_reference_gap_stall_is_advisory_when_native_chain_progress_is_safe_without_recent_paid_work(self) -> None: + now = 1_779_200_000 + payload = self.evm_gap_payload(lag=14_140, local=11_691_000) + payload["sync_health"] = {"native_chain_progress_safe": True} + payload["sync_progress"]["nodes"] = { + "node": {"native_template_health": self.native_template_health(True)} + } + with tempfile.TemporaryDirectory() as tmpdir: + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE = pathlib.Path(tmpdir) / "evm-gap-watch.json" + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE.write_text( + json.dumps( + { + "candidate": True, + "best_lag_blocks": 14_100, + "first_unimproved_epoch": now - 901, + } + ), + encoding="utf-8", + ) + with mock.patch.object(status_sampler.time, "time", return_value=now): + decision = status_sampler.chain_state_restore_decision(payload) + + self.assertFalse(decision["should_repair"]) + self.assertFalse(decision["evm_reference_gap"]["restore_required"]) + self.assertTrue(decision["evm_reference_gap"]["would_restore_required"]) + self.assertTrue(decision["evm_reference_gap"]["restore_suppressed_by_native_mining_safety"]) + self.assertIn("native mining safety", decision["evm_reference_gap"]["reason"]) + + def test_evm_reference_gap_still_restores_when_native_template_proof_is_unsafe(self) -> None: + now = 1_779_200_000 + payload = self.evm_gap_payload(lag=14_140, local=11_691_000) + payload["sync_progress"]["nodes"] = { + "node": {"native_template_health": self.native_template_health(False)} + } + with tempfile.TemporaryDirectory() as tmpdir: + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE = pathlib.Path(tmpdir) / "evm-gap-watch.json" + status_sampler.EVM_REFERENCE_GAP_WATCH_FILE.write_text( + json.dumps( + { + "candidate": True, + "best_lag_blocks": 14_100, + "first_unimproved_epoch": now - 901, + } + ), + encoding="utf-8", + ) + with mock.patch.object(status_sampler.time, "time", return_value=now): + decision = status_sampler.chain_state_restore_decision(payload) + + self.assertTrue(decision["should_repair"]) + self.assertTrue(decision["evm_reference_gap"]["restore_required"]) + self.assertFalse(decision["evm_reference_gap"]["restore_suppressed_by_native_mining_safety"]) + + def test_stalled_import_watch_resets_invalid_zero_epoch(self) -> None: + now = 1_779_200_000 + with tempfile.TemporaryDirectory() as tmpdir: + status_sampler.CHAIN_STATE_IMPORT_WATCH_FILE = pathlib.Path(tmpdir) / "import-watch.json" + status_sampler.CHAIN_STATE_IMPORT_WATCH_FILE.write_text( + json.dumps( + { + "candidate": True, + "height": 12_115_316, + "lag_blocks": 1_260, + "first_stalled_epoch": 0, + "min_lag_blocks": 1_100, + "max_lag_blocks": 1_260, + } + ), + encoding="utf-8", + ) + with mock.patch.object(status_sampler.time, "time", return_value=now): + state = status_sampler.update_stalled_import_watch( + self.native_peer_lag_payload(height=12_115_316, peer_lag=1_260) + ) + + self.assertTrue(state["candidate"]) + self.assertTrue(state["native_p2p_lag_evidence"]) + self.assertEqual(now, state["first_stalled_epoch"]) + self.assertEqual(0, state["stalled_seconds"]) + self.assertFalse(state["restore_required"]) + self.assertIn("invalid previous stall epoch reset", state["reason"]) + + def test_stalled_import_watch_ignores_public_gap_without_native_peer_evidence(self) -> None: + now = 1_779_200_000 + with tempfile.TemporaryDirectory() as tmpdir: + status_sampler.CHAIN_STATE_IMPORT_WATCH_FILE = pathlib.Path(tmpdir) / "import-watch.json" + status_sampler.CHAIN_STATE_IMPORT_WATCH_FILE.write_text( + json.dumps( + { + "candidate": True, + "height": 11_691_000, + "lag_blocks": 14_140, + "first_stalled_epoch": now - 3600, + "min_lag_blocks": 14_000, + "max_lag_blocks": 14_140, + } + ), + encoding="utf-8", + ) + with mock.patch.object(status_sampler.time, "time", return_value=now): + state = status_sampler.update_stalled_import_watch( + self.evm_gap_payload(lag=14_140, local=11_691_000) + ) + + self.assertFalse(state["candidate"]) + self.assertFalse(state["native_p2p_lag_evidence"]) + self.assertEqual(0, state["first_stalled_epoch"]) + self.assertFalse(state["restore_required"]) + self.assertIn("no active native P2P peer-lag evidence", state["reason"]) + + def test_cached_evm_restore_flag_is_not_hard_when_native_paid_mining_is_safe(self) -> None: + payload = self.evm_gap_payload(lag=14_140, local=11_691_000) + payload["sync_health"] = { + "needs_chain_data_restore": True, + "evm_reference_gap_stalled": True, + "evm_reference_gap_watch": {"restore_required": True}, + "pool_has_recent_paid_work": True, + } + payload["sync_progress"]["nodes"] = { + "node": {"native_template_health": self.native_template_health(True)} + } + + self.assertEqual([], status_sampler.chain_state_restore_hard_reasons(payload)) + + def test_cached_evm_restore_flag_is_not_hard_when_native_chain_progress_is_safe_without_paid_work(self) -> None: + payload = self.evm_gap_payload(lag=14_140, local=11_691_000) + payload["sync_health"] = { + "needs_chain_data_restore": True, + "evm_reference_gap_stalled": True, + "evm_reference_gap_watch": {"restore_required": True}, + "native_chain_progress_safe": True, + } + payload["sync_progress"]["nodes"] = { + "node": {"native_template_health": self.native_template_health(True)} + } + + self.assertEqual([], status_sampler.chain_state_restore_hard_reasons(payload)) + def test_evm_reference_gap_watch_resets_when_gap_closes_enough(self) -> None: now = 1_779_200_000 with tempfile.TemporaryDirectory() as tmpdir: @@ -277,7 +508,7 @@ def test_visible_asic_lan_neighbor_does_not_start_pool_while_syncing(self) -> No self.assertFalse(self.pool_compose_start_seen(commands)) self.assertNotIn(f"started_container:{status_sampler.POOL_CONTAINER}", repair["actions"]) - def test_synced_status_without_canonical_proof_does_not_start_pool(self) -> None: + def test_synced_status_without_native_proof_does_not_start_pool(self) -> None: commands = [] incidents = [] status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] @@ -373,7 +604,7 @@ def test_does_not_start_pool_without_miner_demand_or_ready_chain(self) -> None: self.assertFalse(self.pool_compose_start_seen(commands)) self.assertEqual(repair["actions"], []) - def test_catchup_policy_pauses_on_syncing_even_when_mining_ready(self) -> None: + def test_catchup_policy_does_not_pause_on_syncing_when_mining_ready(self) -> None: payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=5) payload["overall"] = "ok" payload["sync_warnings"] = [] @@ -383,10 +614,163 @@ def test_catchup_policy_pauses_on_syncing_even_when_mining_ready(self) -> None: policy = status_sampler.catchup_policy_from_payload(payload) + self.assertFalse(policy["active"]) + self.assertFalse(policy["syncing_active"]) + self.assertEqual(policy["trigger"], "") + self.assertEqual(policy["lag_blocks"], 5) + + def test_catchup_policy_treats_remaining_blocks_as_advisory_when_paid_work_is_recent(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["sync_health"] = {"pool_has_recent_paid_work": True} + payload["catchup_policy"] = { + "active": True, + "syncing_active": True, + "lag_blocks": 14_982, + "threshold_blocks": 300, + } + payload["sync_progress"]["nodes"] = { + "node": {"remaining_blocks": 14_982, "peer_ahead_blocks": 24} + } + + policy = status_sampler.catchup_policy_from_payload(payload) + + self.assertFalse(policy["active"]) + self.assertFalse(policy["syncing_active"]) + self.assertTrue(policy["mining_ready"]) + self.assertTrue(policy["remaining_blocks_advisory"]) + self.assertEqual(policy["lag_blocks"], 24) + self.assertEqual(policy["trigger"], "") + + def test_catchup_policy_treats_remaining_blocks_as_advisory_when_native_chain_progress_is_safe(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["sync_health"] = {"native_chain_progress_safe": True} + payload["catchup_policy"] = { + "active": True, + "syncing_active": True, + "lag_blocks": 14_982, + "threshold_blocks": 300, + } + payload["sync_progress"]["nodes"] = { + "node": { + "remaining_blocks": 14_982, + "peer_ahead_blocks": 4, + "native_template_health": self.native_template_health(True), + } + } + + policy = status_sampler.catchup_policy_from_payload(payload) + + self.assertFalse(policy["active"]) + self.assertFalse(policy["syncing_active"]) + self.assertTrue(policy["mining_ready"]) + self.assertTrue(policy["remaining_blocks_advisory"]) + self.assertTrue(policy["native_advisory_safe"]) + self.assertEqual(policy["lag_blocks"], 4) + self.assertEqual(policy["trigger"], "") + + def test_catchup_policy_keeps_pause_when_native_template_proof_is_unsafe(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["catchup_policy"] = { + "active": True, + "syncing_active": True, + "lag_blocks": 14_982, + "threshold_blocks": 300, + } + payload["sync_progress"]["nodes"] = { + "node": { + "remaining_blocks": 14_982, + "peer_ahead_blocks": 14_982, + "native_template_health": self.native_template_health(False), + } + } + + policy = status_sampler.catchup_policy_from_payload(payload) + self.assertTrue(policy["active"]) self.assertTrue(policy["syncing_active"]) + self.assertFalse(policy["native_advisory_safe"]) + self.assertFalse(policy["remaining_blocks_advisory"]) self.assertEqual(policy["trigger"], "node_syncing") - self.assertEqual(policy["lag_blocks"], 5) + + def test_apply_catchup_node_runtime_skips_recent_paid_work(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["sync_health"] = {"pool_has_recent_paid_work": True} + status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("runtime env must not be changed while paid work is recent") + ) + status_sampler.run = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("node must not be recreated while paid work is recent") + ) + + applied = status_sampler.apply_catchup_node_runtime( + payload, + {"active": True, "lag_blocks": 14_982, "threshold_blocks": 300}, + ) + + self.assertFalse(applied) + + def test_apply_catchup_node_runtime_skips_native_safe_evm_advisory_sync(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["sync_health"] = {"native_chain_progress_safe": True} + payload["sync_progress"]["nodes"] = { + "node": { + "remaining_blocks": 14_982, + "peer_ahead_blocks": 4, + "native_template_health": self.native_template_health(True), + } + } + status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("runtime env must not be changed when native mining safety is proven") + ) + status_sampler.run = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("node must not be recreated when native mining safety is proven") + ) + + applied = status_sampler.apply_catchup_node_runtime( + payload, + status_sampler.catchup_policy_from_payload(payload), + ) + + self.assertFalse(applied) + + def test_apply_catchup_node_runtime_skips_stale_paid_work_evidence(self) -> None: + payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=14_982) + payload["sync_health"] = { + "pool_paid_work_state": { + "accepted_block_recent": False, + "accepted_block_submissions": 2061, + "last_accepted_age_seconds": 69.978, + } + } + status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("runtime env must not be changed while accepted block history exists") + ) + status_sampler.run = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("node must not be recreated while accepted block history exists") + ) + + applied = status_sampler.apply_catchup_node_runtime( + payload, + {"active": True, "lag_blocks": 14_982, "threshold_blocks": 300}, + ) + + self.assertFalse(applied) + + def test_recreate_node_services_blocks_running_pool(self) -> None: + commands = [] + status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + os.environ["BDAG_NODE_SERVICES"] = "node" + payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) + payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True + status_sampler.run = lambda command, timeout=20: commands.append(command) or self.command_result(command) + + ok, results = status_sampler.recreate_node_services(payload, "unit test blocked recreate") + + self.assertFalse(ok) + self.assertEqual(results[0]["service"], "node") + self.assertTrue(results[0]["blocked"]) + self.assertIn("paid-block evidence", results[0]["blocked_reason"]) + self.assertEqual(commands, []) def test_syncing_node_leaves_running_pool_up_below_lag_threshold(self) -> None: commands = [] @@ -394,7 +778,10 @@ def test_syncing_node_leaves_running_pool_up_below_lag_threshold(self) -> None: status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] os.environ["BDAG_ENABLE_NODE_MINING"] = "1" os.environ["BDAG_NODE_MODULES"] = "Blockdag,miner" - os.environ["BDAG_NODE_MINING_ARGS"] = "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + os.environ["BDAG_NODE_MINING_ARGS"] = ( + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc " + "--obsoleteheight=20" + ) os.environ["NODE_ARGS_APPEND"] = os.environ["BDAG_NODE_MINING_ARGS"] payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=5) payload["overall"] = "ok" @@ -415,19 +802,22 @@ def fake_set_runtime_env(key: str, value: str): status_sampler.PROJECT_ROOT = pathlib.Path(tmp) repair = status_sampler.mining_imperative_repair(payload) - self.assertIn(f"template_pause:{status_sampler.POOL_CONTAINER}:catchup_pause", repair["actions"]) + self.assertNotIn(f"template_pause:{status_sampler.POOL_CONTAINER}:catchup_pause", repair["actions"]) self.assertNotIn(f"stopped_container:{status_sampler.POOL_CONTAINER}:catchup_pause", repair["actions"]) - self.assertIn("applied_catchup_node_runtime", repair["actions"]) - self.assertEqual(env_updates["BDAG_ENABLE_NODE_MINING"], "0") + self.assertNotIn("applied_catchup_node_runtime", repair["actions"]) + self.assertNotIn("BDAG_ENABLE_NODE_MINING", env_updates) self.assertFalse(any(command[-2:] == ["stop", status_sampler.POOL_CONTAINER] for command in commands)) - def test_catchup_pause_leaves_pool_running_and_removes_node_mining_churn(self) -> None: + def test_catchup_pause_leaves_pool_running_without_node_runtime_mutation(self) -> None: commands = [] env_updates = {} status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] os.environ["BDAG_ENABLE_NODE_MINING"] = "1" os.environ["BDAG_NODE_MODULES"] = "Blockdag,miner" - os.environ["BDAG_NODE_MINING_ARGS"] = "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + os.environ["BDAG_NODE_MINING_ARGS"] = ( + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc " + "--obsoleteheight=20" + ) os.environ["NODE_ARGS_APPEND"] = os.environ["BDAG_NODE_MINING_ARGS"] os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="syncing", remaining_blocks=450) @@ -476,18 +866,14 @@ def fake_run(command: list[str], timeout: int = 20): self.assertIn(f"template_pause:{status_sampler.POOL_CONTAINER}:catchup_pause", repair["actions"]) self.assertNotIn(f"stopped_container:{status_sampler.POOL_CONTAINER}:catchup_pause", repair["actions"]) - self.assertIn("applied_catchup_node_runtime", repair["actions"]) - self.assertEqual(env_updates["BDAG_ENABLE_NODE_MINING"], "0") - self.assertEqual(env_updates.get("BDAG_NODE_MODULES", os.environ["BDAG_NODE_MODULES"]), "Blockdag,miner") - self.assertEqual(env_updates["BDAG_NODE_MINING_ARGS"], "") - self.assertEqual(env_updates["NODE_ARGS_APPEND"], "") - self.assertIn("cache=6144", node_conf) - self.assertIn("--cache 6144", node_conf) - self.assertIn("miningaddr=", node_conf) - self.assertIn("modules=miner", node_conf) - self.assertIn("# miner=true disabled during catch-up pause", node_conf) + self.assertNotIn("applied_catchup_node_runtime", repair["actions"]) + self.assertEqual(env_updates, {}) + self.assertIn("cache=2048", node_conf) + self.assertIn("--cache 2048", node_conf) + self.assertIn("miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc", node_conf) + self.assertIn("miner=true", node_conf) self.assertFalse(any(command[-2:] == ["stop", status_sampler.POOL_CONTAINER] for command in commands)) - self.assertTrue(any("--force-recreate" in command for command in commands)) + self.assertFalse(any("--force-recreate" in command for command in commands)) def test_catchup_pause_does_not_restart_stopped_pool_for_visible_miners(self) -> None: commands = [] @@ -558,6 +944,28 @@ def test_repairs_missing_tracked_miners_from_pool_activity(self) -> None: self.assertIn("repaired_tracked_miners", repair["actions"]) + def test_no_logs_pool_metrics_miner_demand_is_not_a_tracking_gap(self) -> None: + status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) + payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True + payload["pool"]["metrics"]["active_connections"] = 4 + payload["pool_metrics"]["active_connections"] = 4 + payload["pool"]["source_job_health"] = {"authorized_miners": 4, "ready_miners": 4} + payload["miner_health"] = { + "failures": [], + "warnings": [], + "miners": [], + "connected_count_effective": 4, + "connected_count_source": "pool-metrics", + } + status_sampler.collect_pool_activity = lambda lines=0: (_ for _ in ()).throw( + AssertionError("pool-metrics fallback must not trigger tracked-miner repair") + ) + + repair = status_sampler.mining_imperative_repair(payload) + + self.assertNotIn("repaired_tracked_miners", repair["actions"]) + def test_detects_miner_activity_visibility_gap_after_power_cycle(self) -> None: payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) payload["miner_health"] = { @@ -654,13 +1062,13 @@ def test_enables_node_mining_template_support_when_miner_is_present(self) -> Non commands = [] env_updates = {} status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + status_sampler.MINING_IMPERATIVE_START_POOL_ENABLED = False os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" os.environ["BDAG_ENABLE_NODE_MINING"] = "0" os.environ["BDAG_NODE_MODULES"] = "Blockdag" os.environ["BDAG_NODE_MINING_ARGS"] = "" os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} def fake_set_runtime_env(key: str, value: str): @@ -683,14 +1091,15 @@ def fake_run(command: list[str], timeout: int = 20): self.assertIn("--miner", env_updates["NODE_ARGS_APPEND"]) self.assertIn("--miner", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertIn("--miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertIn("--obsoleteheight=20", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertNotIn("--miningnopendingtx", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowminingwhennearlysynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowsubmitwhennotsynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertEqual(env_updates["NODE_ARGS_APPEND"], env_updates["BDAG_NODE_MINING_ARGS"]) self.assertTrue(any("--force-recreate" in command for command in commands)) - def test_node_mining_template_support_requires_canonical_proof(self) -> None: + def test_node_mining_template_support_requires_native_proof(self) -> None: commands = [] - incidents = [] status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" os.environ["BDAG_ENABLE_NODE_MINING"] = "0" @@ -702,18 +1111,61 @@ def test_node_mining_template_support_requires_canonical_proof(self) -> None: payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( - AssertionError("config edit must not run without canonical proof") + AssertionError("config edit must not run without native proof") ) status_sampler.run = lambda command, timeout=20: commands.append(command) or self.command_result(command) - status_sampler.append_incident = ( - lambda event_type, severity, *_args, **_kwargs: incidents.append((event_type, severity)) + + repair = status_sampler.mining_imperative_repair(payload) + + self.assertNotIn("enabled_node_mining_template_support", repair["actions"]) + self.assertFalse(any("--force-recreate" in command for command in commands)) + + def test_recent_paid_work_defers_node_mining_template_repair(self) -> None: + status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + os.environ["BDAG_ENABLE_NODE_MINING"] = "0" + os.environ["BDAG_NODE_MODULES"] = "Blockdag" + os.environ["BDAG_NODE_MINING_ARGS"] = "" + os.environ["NODE_ARGS_APPEND"] = "" + payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) + payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True + payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} + payload["sync_health"] = {"pool_has_recent_paid_work": True} + status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("fresh paid block submissions should defer node config repair") ) repair = status_sampler.mining_imperative_repair(payload) self.assertNotIn("enabled_node_mining_template_support", repair["actions"]) + + def test_stale_paid_work_evidence_blocks_live_node_mining_template_recreate(self) -> None: + commands = [] + status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + os.environ["BDAG_ENABLE_NODE_MINING"] = "0" + os.environ["BDAG_NODE_MODULES"] = "Blockdag" + os.environ["BDAG_NODE_MINING_ARGS"] = "" + os.environ["NODE_ARGS_APPEND"] = "" + payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) + payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True + payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} + payload["sync_health"] = { + "pool_paid_work_state": { + "accepted_block_recent": False, + "accepted_block_submissions": 2061, + "last_accepted_age_seconds": 69.978, + } + } + status_sampler.set_runtime_env_value = lambda *_args, **_kwargs: (_ for _ in ()).throw( + AssertionError("stale-but-present accepted block evidence must block live node config repair") + ) + status_sampler.run = lambda command, timeout=20: commands.append(command) or self.command_result(command) + + repair = status_sampler.mining_imperative_repair(payload) + + self.assertNotIn("enabled_node_mining_template_support", repair["actions"]) self.assertFalse(any("--force-recreate" in command for command in commands)) - self.assertIn(("mining_imperative_node_mining_gate_blocked", "warning"), incidents) def test_node_mining_template_repair_preserves_node_conf_miner_module(self) -> None: commands = [] @@ -725,7 +1177,6 @@ def test_node_mining_template_repair_preserves_node_conf_miner_module(self) -> N os.environ["BDAG_NODE_MINING_ARGS"] = "" os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} def fake_set_runtime_env(key: str, value: str): @@ -753,7 +1204,7 @@ def fake_set_runtime_env(key: str, value: str): def test_node_args_parser_accepts_nodeworker_embedded_node_args(self) -> None: address = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" - command_line = f"nodeworker --node-args=--miner --miningaddr={address}" + command_line = f"nodeworker --node-args=--miner --miningaddr={address} --obsoleteheight=20" self.assertTrue(status_sampler.node_mining_args_are_safe_and_complete(command_line, address)) @@ -770,7 +1221,6 @@ def test_repairs_node_mining_args_when_unsafe_sync_bypass_is_present(self) -> No ) os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} def fake_set_runtime_env(key: str, value: str): @@ -785,6 +1235,8 @@ def fake_set_runtime_env(key: str, value: str): self.assertIn("enabled_node_mining_template_support", repair["actions"]) self.assertIn("--miner", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertIn("--obsoleteheight=20", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertNotIn("--miningnopendingtx", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowminingwhennearlysynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowsubmitwhennotsynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertEqual(env_updates["NODE_ARGS_APPEND"], env_updates["BDAG_NODE_MINING_ARGS"]) @@ -794,15 +1246,16 @@ def test_keeps_configured_miner_rpc_module(self) -> None: commands = [] env_updates = {} status_sampler.MINING_IMPERATIVE_GUARD_UNITS = [] + status_sampler.MINING_IMPERATIVE_START_POOL_ENABLED = False os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" os.environ["BDAG_ENABLE_NODE_MINING"] = "1" os.environ["BDAG_NODE_MODULES"] = "Blockdag,miner" os.environ["BDAG_NODE_MINING_ARGS"] = ( - "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc " + "--obsoleteheight=20" ) os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} def fake_set_runtime_env(key: str, value: str): @@ -827,11 +1280,11 @@ def test_recreates_node_when_live_process_has_unsafe_sync_bypass(self) -> None: os.environ["BDAG_ENABLE_NODE_MINING"] = "1" os.environ["BDAG_NODE_MODULES"] = "Blockdag" os.environ["BDAG_NODE_MINING_ARGS"] = ( - "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc " + "--obsoleteheight=20" ) os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} def fake_set_runtime_env(key: str, value: str): @@ -856,6 +1309,8 @@ def fake_run(command: list[str], timeout: int = 20): self.assertIn("enabled_node_mining_template_support", repair["actions"]) self.assertIn("--miner", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertIn("--obsoleteheight=20", env_updates["BDAG_NODE_MINING_ARGS"]) + self.assertNotIn("--miningnopendingtx", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowminingwhennearlysynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertNotIn("--allowsubmitwhennotsynced", env_updates["BDAG_NODE_MINING_ARGS"]) self.assertEqual(env_updates["NODE_ARGS_APPEND"], env_updates["BDAG_NODE_MINING_ARGS"]) @@ -887,14 +1342,14 @@ def test_quarantines_fastsync_peer_returning_only_orphan_blocks(self) -> None: os.environ["BDAG_NODE_MODULES"] = "Blockdag" os.environ["MINING_ADDRESS"] = "0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc" os.environ["BDAG_NODE_MINING_ARGS"] = ( - "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc --maxinbound=1" + "--miner --miningaddr=0xA1Ee1005c4Ff181e93e717D2C624554b66AB7DFc " + "--obsoleteheight=20 --maxinbound=1" ) os.environ["BDAG_NODE_PEER_ADDRESSES"] = f"/ip4/10.0.0.2/tcp/8151/p2p/{peer_id},/ip4/3.3.3.3/tcp/8150/p2p/good" os.environ["BDAG_FASTSYNC_PEERS"] = f"/ip4/10.0.0.2/tcp/8151/p2p/{peer_id}" os.environ["BOOTSTRAP_PEER_ADDRESSES"] = f"/ip4/10.0.0.2/tcp/8151/p2p/{peer_id},/ip4/4.4.4.4/tcp/8150/p2p/good" os.environ["BDAG_NODE_SERVICES"] = "node" payload = self.stopped_pool_payload(sync_status="synced", remaining_blocks=0) - payload["containers"][status_sampler.POOL_CONTAINER]["running"] = True payload["miner_health"] = {"tracked_count": 1, "connected_count": 1, "managed_count": 1} payload["nodes"] = { "node": { diff --git a/ops/tests/test_watchdog_sync_restart.py b/ops/tests/test_watchdog_sync_restart.py index a23c92c..a599b31 100644 --- a/ops/tests/test_watchdog_sync_restart.py +++ b/ops/tests/test_watchdog_sync_restart.py @@ -8,6 +8,7 @@ import sys import tempfile import unittest +from datetime import datetime, timezone from unittest import mock OPS_DIR = pathlib.Path(__file__).resolve().parents[1] @@ -41,6 +42,10 @@ def node_status(*, importing: bool, last_import_age_seconds: int, latest_block: } +def docker_started_at(epoch_seconds: int) -> str: + return datetime.fromtimestamp(epoch_seconds, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S") + ".000000000Z" + + class WatchdogSyncRestartTests(unittest.TestCase): def test_active_import_requires_fresh_import_age_when_importing_flag_is_stuck(self) -> None: now = 1_779_200_000 @@ -104,6 +109,35 @@ def test_active_import_suppression_does_not_consume_repair_cooldown(self) -> Non self.assertNotIn("last_sync_repair_at", state) self.assertIn("last_sync_repair_suppressed_epoch", state) + def test_active_import_does_not_suppress_when_native_p2p_is_down(self) -> None: + now = 1_779_200_000 + status = node_status(importing=True, last_import_age_seconds=40) + status["sync_progress"]["status"] = "p2p_down" + status["sync_progress"]["error"] = "syncing but no active peers available" + status["sync_progress"]["peer_count"] = 0 + status["sync_progress"]["p2p_connections"] = 0 + status["sync_progress"]["nodes"]["node"]["status"] = "p2p_down" + status["sync_progress"]["nodes"]["node"]["error"] = "no active native P2P peers" + status["sync_progress"]["nodes"]["node"]["peer_count"] = 0 + status["sync_progress"]["nodes"]["node"]["p2p_connections"] = 0 + state = { + "last_sync_height_changed_at_by_node": {"node": now - 700}, + } + + with mock.patch.object(watchdog, "NODES", ["node"]), mock.patch.object( + watchdog, "log", lambda _message: None + ), mock.patch.object(watchdog, "record_efficiency_event", lambda *_args, **_kwargs: None): + suppressed = watchdog.suppress_sync_restart_for_active_import( + status, + state, + "waiting for node sync", + "node", + ) + + self.assertFalse(suppressed) + self.assertIn("last_sync_repair_suppression_bypassed_epoch", state) + self.assertIn("native P2P", state["last_sync_repair_suppression_bypassed_reason"]) + def test_check_once_active_import_suppression_does_not_consume_repair_cooldown(self) -> None: now = 1_779_200_000 status = { @@ -156,6 +190,330 @@ def test_check_once_active_import_suppression_does_not_consume_repair_cooldown(s self.assertIn("last_sync_repair_suppressed_epoch", result["watchdog_state"]) self.assertTrue(written) + def test_check_once_normalizes_compact_status_payload_without_failures(self) -> None: + now = 1_779_200_000 + status = { + "status": "ok", + "pool_health": {}, + "miner_health": {"connected_count": 0, "connected_count_effective": 0, "miners": []}, + "sync_progress": {"status": "synced"}, + } + state: dict[str, object] = {} + written: list[dict[str, object]] = [] + + with mock.patch.object(watchdog.time, "time", return_value=now), mock.patch.object( + watchdog, "NODES", ["node"] + ), mock.patch.object(watchdog, "read_state", return_value=state), mock.patch.object( + watchdog, "write_state", side_effect=lambda payload: written.append(dict(payload)) + ), mock.patch.object( + watchdog, "collect_stack_status", return_value=status + ), mock.patch.object( + watchdog, "lock_is_held", return_value=False + ), mock.patch.object( + watchdog, "record_earnings_snapshot", return_value={} + ), mock.patch.object( + watchdog, "status_payload_has_tracking_gap", return_value=False + ), mock.patch.object( + watchdog, "node_mining_template_support_should_repair", return_value=False + ), mock.patch.object( + watchdog, "fastsync_peer_quarantine_should_repair", return_value=False + ), mock.patch.object( + watchdog, "log", lambda _message: None + ): + result = watchdog.check_once(3, 1800, 5, 900, repair=True) + + self.assertEqual("ok", result["watchdog_state"]["last_status"]) + self.assertEqual([], result["watchdog_state"]["last_failures"]) + self.assertTrue(written) + + def test_check_once_waits_before_restarting_node_when_active_import_has_no_native_peers(self) -> None: + now = 1_779_200_000 + status = { + **node_status(importing=True, last_import_age_seconds=40), + "failures": [], + "stack_failures": [], + "miner_failures": [], + "warnings": ["pool is waiting for node sync to finish"], + "overall": "syncing", + "mining_address": "0x1111111111111111111111111111111111111111", + "pool_health": {"initial_download": True}, + "miner_health": {"connected_count": 0, "connected_count_effective": 0, "miners": []}, + } + status["sync_progress"]["status"] = "p2p_down" + status["sync_progress"]["error"] = "syncing but no active peers available" + status["sync_progress"]["peer_count"] = 0 + status["sync_progress"]["p2p_connections"] = 0 + status["sync_progress"]["nodes"]["node"]["status"] = "p2p_down" + status["sync_progress"]["nodes"]["node"]["error"] = "no active native P2P peers" + status["sync_progress"]["nodes"]["node"]["peer_count"] = 0 + status["sync_progress"]["nodes"]["node"]["p2p_connections"] = 0 + state = { + "consecutive_syncing": 4, + "last_sync_height_by_node": {"node": 1000}, + "last_sync_height_changed_at_by_node": {"node": now - 700}, + } + written: list[dict[str, object]] = [] + restarts: list[tuple[str, str]] = [] + + with mock.patch.object(watchdog.time, "time", return_value=now), mock.patch.object( + watchdog, "NODES", ["node"] + ), mock.patch.object(watchdog, "read_state", return_value=state), mock.patch.object( + watchdog, "write_state", side_effect=lambda payload: written.append(dict(payload)) + ), mock.patch.object( + watchdog, "collect_stack_status", return_value=status + ), mock.patch.object( + watchdog, "lock_is_held", return_value=False + ), mock.patch.object( + watchdog, "record_earnings_snapshot", return_value={} + ), mock.patch.object( + watchdog, "status_payload_has_tracking_gap", return_value=False + ), mock.patch.object( + watchdog, "node_mining_template_support_should_repair", return_value=False + ), mock.patch.object( + watchdog, "fastsync_peer_quarantine_should_repair", return_value=False + ), mock.patch.object( + watchdog, "record_efficiency_event", lambda *_args, **_kwargs: None + ), mock.patch.object( + watchdog, "log", lambda _message: None + ), mock.patch.object( + watchdog, "run_node_restart", side_effect=lambda node, reason: restarts.append((node, reason)) or True + ), mock.patch.object( + watchdog, "run_repair", side_effect=AssertionError("peer loss should use targeted node restart") + ): + result = watchdog.check_once(3, 1800, 5, 900, repair=True) + + self.assertEqual("p2p_down", result["watchdog_state"]["last_status"]) + self.assertEqual([], restarts) + self.assertIn("native P2P", "; ".join(result["watchdog_state"]["last_sync_warnings"])) + self.assertEqual(5, result["watchdog_state"]["consecutive_syncing"]) + self.assertEqual(now, result["watchdog_state"]["native_p2p_peer_loss_since_epoch"]) + self.assertEqual(0, result["watchdog_state"]["native_p2p_peer_loss_age_seconds"]) + self.assertNotIn("last_sync_repair_at", result["watchdog_state"]) + self.assertTrue(written) + + def test_check_once_suppresses_2044_node_restart_replay_during_active_safe_mining(self) -> None: + now = 1_779_200_000 + status = { + "failures": [], + "stack_failures": [], + "miner_failures": [], + "warnings": [ + "catch-up pause active: chain node is 17380 blocks behind peers", + "pool source job health reports zero ready miners while 4 miner(s) are connected", + ], + "overall": "syncing", + "mining_address": "0x1111111111111111111111111111111111111111", + "pool_health": { + "initial_download": False, + "block_submit_success_count": 1102, + "last_block_submit_age_seconds": 75, + }, + "miner_health": { + "connected_count": 4, + "connected_count_effective": 4, + "miners": [], + }, + "sync_progress": { + "status": "syncing", + "remaining_blocks": 420, + "nodes": {"node": {"current_block": 12184530, "remaining_blocks": 420, "status": "syncing"}}, + }, + "sync_health": { + "needs_fast_sync_repair": True, + "native_chain_progress_safe": True, + "pool_paid_work_state": { + "accepted_block_submissions": 1102, + "accepted_block_recent": False, + "last_accepted_age_seconds": 75, + }, + }, + "canonical_mining_safety": { + "safe": True, + "public_chain_diverged": False, + }, + } + state = { + "consecutive_syncing": 5, + "last_sync_height_by_node": {"node": 12184530}, + "last_sync_height_changed_at_by_node": {"node": now - 120}, + } + written: list[dict[str, object]] = [] + + with mock.patch.object(watchdog.time, "time", return_value=now), mock.patch.object( + watchdog, "NODES", ["node"] + ), mock.patch.object(watchdog, "read_state", return_value=state), mock.patch.object( + watchdog, "write_state", side_effect=lambda payload: written.append(dict(payload)) + ), mock.patch.object( + watchdog, "collect_stack_status", return_value=status + ), mock.patch.object( + watchdog, "lock_is_held", return_value=False + ), mock.patch.object( + watchdog, "record_earnings_snapshot", return_value={} + ), mock.patch.object( + watchdog, "status_payload_has_tracking_gap", return_value=False + ), mock.patch.object( + watchdog, "node_mining_template_support_should_repair", return_value=False + ), mock.patch.object( + watchdog, "fastsync_peer_quarantine_should_repair", return_value=False + ), mock.patch.object( + watchdog, "record_efficiency_event", lambda *_args, **_kwargs: None + ), mock.patch.object( + watchdog, "log", lambda _message: None + ), mock.patch.object( + watchdog, "run_node_restart", side_effect=AssertionError("20:44 replay must not restart node") + ), mock.patch.object( + watchdog, "run_repair", side_effect=AssertionError("20:44 replay must not restart stack") + ): + result = watchdog.check_once(3, 1800, 5, 900, repair=True) + + self.assertEqual("syncing", result["watchdog_state"]["last_status"]) + self.assertIn("last_sync_repair_suppressed_reason", result["watchdog_state"]) + self.assertIn("accepted block work", result["watchdog_state"]["last_sync_repair_suppressed_reason"]) + self.assertNotIn("last_sync_repair_at", result["watchdog_state"]) + self.assertTrue(written) + + def test_check_once_resets_stale_peer_loss_timer_after_node_restart(self) -> None: + now = 1_779_200_000 + status = { + **node_status(importing=False, last_import_age_seconds=700), + "failures": [], + "stack_failures": [], + "miner_failures": [], + "warnings": ["pool source job health reports zero ready miners while 4 miner(s) are connected"], + "overall": "syncing", + "containers": { + "node": { + "running": True, + "started_at": docker_started_at(now - 40), + } + }, + "miner_health": {"connected_count": 4, "connected_count_effective": 4, "miners": []}, + "pool_health": { + "initial_download": False, + "source_selected_backend_p2p_fresh": False, + "source_selected_backend_submit_ready": False, + }, + } + status["sync_progress"]["status"] = "p2p_down" + status["sync_progress"]["error"] = "syncing but no active peers available" + status["sync_progress"]["peer_count"] = 0 + status["sync_progress"]["p2p_connections"] = 0 + status["sync_progress"]["nodes"]["node"]["status"] = "p2p_down" + status["sync_progress"]["nodes"]["node"]["error"] = "no active native P2P peers" + status["sync_progress"]["nodes"]["node"]["peer_count"] = 0 + status["sync_progress"]["nodes"]["node"]["p2p_connections"] = 0 + state = { + "consecutive_syncing": 5, + "native_p2p_peer_loss_since_epoch": now - watchdog.DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS - 120, + "native_p2p_peer_loss_reason": "previous peer-loss sample", + } + written: list[dict[str, object]] = [] + + with mock.patch.object(watchdog.time, "time", return_value=now), mock.patch.object( + watchdog, "NODES", ["node"] + ), mock.patch.object(watchdog, "read_state", return_value=state), mock.patch.object( + watchdog, "write_state", side_effect=lambda payload: written.append(dict(payload)) + ), mock.patch.object( + watchdog, "collect_stack_status", return_value=status + ), mock.patch.object( + watchdog, "lock_is_held", return_value=False + ), mock.patch.object( + watchdog, "record_earnings_snapshot", return_value={} + ), mock.patch.object( + watchdog, "status_payload_has_tracking_gap", return_value=False + ), mock.patch.object( + watchdog, "node_mining_template_support_should_repair", return_value=False + ), mock.patch.object( + watchdog, "fastsync_peer_quarantine_should_repair", return_value=False + ), mock.patch.object( + watchdog, "record_efficiency_event", lambda *_args, **_kwargs: None + ), mock.patch.object( + watchdog, "log", lambda _message: None + ), mock.patch.object( + watchdog, "run_node_restart", side_effect=AssertionError("fresh node boot must reset peer-loss timer") + ), mock.patch.object( + watchdog, "run_repair", side_effect=AssertionError("fresh node boot must not restart stack") + ): + result = watchdog.check_once(3, 1800, 5, 900, repair=True) + + self.assertEqual("p2p_down", result["watchdog_state"]["last_status"]) + self.assertEqual(0, result["watchdog_state"]["native_p2p_peer_loss_age_seconds"]) + self.assertEqual( + "peer-loss timer predates current node start", + result["watchdog_state"]["native_p2p_peer_loss_reset_reason"], + ) + self.assertNotIn("last_sync_repair_at", result["watchdog_state"]) + self.assertTrue(written) + + def test_check_once_restarts_node_after_sustained_native_peer_loss(self) -> None: + now = 1_779_200_000 + status = { + **node_status(importing=True, last_import_age_seconds=40), + "failures": [], + "stack_failures": [], + "miner_failures": [], + "warnings": ["pool is waiting for node sync to finish"], + "overall": "syncing", + "mining_address": "0x1111111111111111111111111111111111111111", + "pool_health": {"initial_download": True}, + "miner_health": {"connected_count": 0, "connected_count_effective": 0, "miners": []}, + } + status["sync_progress"]["status"] = "p2p_down" + status["sync_progress"]["error"] = "syncing but no active peers available" + status["sync_progress"]["peer_count"] = 0 + status["sync_progress"]["p2p_connections"] = 0 + status["sync_progress"]["nodes"]["node"]["status"] = "p2p_down" + status["sync_progress"]["nodes"]["node"]["error"] = "no active native P2P peers" + status["sync_progress"]["nodes"]["node"]["peer_count"] = 0 + status["sync_progress"]["nodes"]["node"]["p2p_connections"] = 0 + state = { + "consecutive_syncing": 4, + "last_sync_height_by_node": {"node": 1000}, + "last_sync_height_changed_at_by_node": {"node": now - 700}, + "native_p2p_peer_loss_since_epoch": now - watchdog.DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS - 1, + "native_p2p_peer_loss_reason": "previous peer-loss sample", + } + written: list[dict[str, object]] = [] + restarts: list[tuple[str, str]] = [] + + with mock.patch.object(watchdog.time, "time", return_value=now), mock.patch.object( + watchdog, "NODES", ["node"] + ), mock.patch.object(watchdog, "read_state", return_value=state), mock.patch.object( + watchdog, "write_state", side_effect=lambda payload: written.append(dict(payload)) + ), mock.patch.object( + watchdog, "collect_stack_status", return_value=status + ), mock.patch.object( + watchdog, "lock_is_held", return_value=False + ), mock.patch.object( + watchdog, "record_earnings_snapshot", return_value={} + ), mock.patch.object( + watchdog, "status_payload_has_tracking_gap", return_value=False + ), mock.patch.object( + watchdog, "node_mining_template_support_should_repair", return_value=False + ), mock.patch.object( + watchdog, "fastsync_peer_quarantine_should_repair", return_value=False + ), mock.patch.object( + watchdog, "record_efficiency_event", lambda *_args, **_kwargs: None + ), mock.patch.object( + watchdog, "log", lambda _message: None + ), mock.patch.object( + watchdog, "run_node_restart", side_effect=lambda node, reason: restarts.append((node, reason)) or True + ), mock.patch.object( + watchdog, "run_repair", side_effect=AssertionError("peer loss should use targeted node restart") + ): + result = watchdog.check_once(3, 1800, 5, 900, repair=True) + + self.assertEqual("p2p_down", result["watchdog_state"]["last_status"]) + self.assertEqual([("node", restarts[0][1])], restarts) + self.assertIn("native P2P", "; ".join(result["watchdog_state"]["last_sync_warnings"])) + self.assertEqual(0, result["watchdog_state"]["consecutive_syncing"]) + self.assertEqual(now, result["watchdog_state"]["last_sync_repair_at"]) + self.assertGreaterEqual( + result["watchdog_state"]["native_p2p_peer_loss_age_seconds"], + watchdog.DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS, + ) + self.assertTrue(written) + def test_check_once_does_not_start_pool_when_catchup_pause_stopped_it(self) -> None: now = 1_779_200_000 pool_failure = f"{watchdog.POOL_CONTAINER} is not running" diff --git a/ops/watchdog.py b/ops/watchdog.py index 83be8c9..8a8ded1 100644 --- a/ops/watchdog.py +++ b/ops/watchdog.py @@ -23,15 +23,19 @@ from stack_status_source import collect_stack_status from pool_ops import ( LOG_DIR, + MINER_HASHRATE_PROBE_TIMEOUT, NODES, POOL_CONTAINER, POOL_ENV_FILE, PROJECT_ROOT, RUNTIME_DIR, action_log_path, + collect_pool_job_state_activity, configure_miner, default_miner_pool_settings, ensure_runtime, + get_miner_cgminer_devs, + get_miner_pools, is_lan_ipv4, now_iso, record_earnings_snapshot, @@ -69,6 +73,12 @@ DEFAULT_ACTIVE_SYNC_IMPORT_GRACE_SECONDS = int( os.environ.get("BDAG_WATCHDOG_ACTIVE_SYNC_IMPORT_GRACE_SECONDS", "300") ) +DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS = int( + os.environ.get("BDAG_WATCHDOG_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS", "300") +) +DEFAULT_ACTIVE_MINING_NODE_RESTART_INHIBIT_SECONDS = int( + os.environ.get("BDAG_WATCHDOG_ACTIVE_MINING_NODE_RESTART_INHIBIT_SECONDS", "900") +) DEFAULT_SHARE_STALL_THRESHOLD = int(os.environ.get("BDAG_WATCHDOG_SHARE_STALL_THRESHOLD", "2")) DEFAULT_SHARE_STALL_RESTART_COOLDOWN = int( os.environ.get("BDAG_WATCHDOG_SHARE_STALL_RESTART_COOLDOWN", os.environ.get("BDAG_SYNCING_RESTART_COOLDOWN", "900")) @@ -96,6 +106,12 @@ DEFAULT_ASIC_API_STALL_REPAIR_COOLDOWN = int( os.environ.get("BDAG_WATCHDOG_ASIC_API_STALL_REPAIR_COOLDOWN", str(DEFAULT_MINER_RESTART_COOLDOWN)) ) +DEFAULT_ASIC_MISSING_LANE_CONFIRM_SECONDS = int( + os.environ.get("BDAG_WATCHDOG_ASIC_MISSING_LANE_CONFIRM_SECONDS", "45") +) +DEFAULT_ASIC_API_STALL_PROBE_TIMEOUT = float( + os.environ.get("BDAG_WATCHDOG_ASIC_API_STALL_PROBE_TIMEOUT", str(MINER_HASHRATE_PROBE_TIMEOUT)) +) DEFAULT_MINER_USEFUL_WORK_STALL_SECONDS = int( os.environ.get("BDAG_WATCHDOG_MINER_USEFUL_WORK_STALL_SECONDS", "150") ) @@ -359,6 +375,94 @@ def float_or_none(value: Any) -> float | None: return None +def list_or_empty(value: Any) -> list[Any]: + return list(value) if isinstance(value, list) else [] + + +def dict_or_empty(value: Any) -> dict[str, Any]: + return value if isinstance(value, dict) else {} + + +def normalize_watchdog_status_payload(status: dict[str, Any]) -> dict[str, Any]: + """Make repair code fail-closed instead of crashing on compact status payloads.""" + normalized = dict(status) + failures = list_or_empty(normalized.get("failures")) + stack_failures = list_or_empty(normalized.get("stack_failures")) + miner_failures = list_or_empty(normalized.get("miner_failures")) + if not failures and "failures" not in normalized and (stack_failures or miner_failures): + failures = [*stack_failures, *miner_failures] + if not stack_failures and "stack_failures" not in normalized: + stack_failures = list(failures) + normalized["failures"] = failures + normalized["stack_failures"] = stack_failures + normalized["miner_failures"] = miner_failures + normalized["warnings"] = list_or_empty(normalized.get("warnings")) + normalized["sync_warnings"] = list_or_empty(normalized.get("sync_warnings", normalized["warnings"])) + normalized["overall"] = str(normalized.get("overall") or normalized.get("status") or "unknown") + if not isinstance(normalized.get("pool_health"), dict) and isinstance(normalized.get("pool"), dict): + normalized["pool_health"] = normalized["pool"] + normalized["pool_health"] = dict_or_empty(normalized.get("pool_health")) + normalized["miner_health"] = dict_or_empty(normalized.get("miner_health")) + normalized["containers"] = dict_or_empty(normalized.get("containers")) + normalized["sync_health"] = dict_or_empty(normalized.get("sync_health")) + normalized["sync_progress"] = dict_or_empty(normalized.get("sync_progress")) + return normalized + + +def native_p2p_peer_loss_reason(status: dict[str, Any]) -> str: + reasons: list[str] = [] + min_fresh_peers = DEFAULT_MINER_USEFUL_WORK_MIN_HEALTHY_PEERS + + def inspect_progress(progress: dict[str, Any], label: str) -> None: + status_text = str(progress.get("status") or "").strip().lower() + error_text = str(progress.get("error") or "").strip().lower() + if status_text == "p2p_down": + reasons.append(f"{label} native P2P status is p2p_down") + if "no active native p2p peers" in error_text or "syncing but no active peers" in error_text: + reasons.append(f"{label} reports no active native P2P peers") + + peer_count = int_or_none(progress.get("peer_count")) + p2p_connections = int_or_none(progress.get("p2p_connections")) + explicit_zero_counts = [ + name + for name, value in (("peer_count", peer_count), ("p2p_connections", p2p_connections)) + if value == 0 + ] + if explicit_zero_counts: + reasons.append(f"{label} has zero native P2P peer count ({', '.join(explicit_zero_counts)})") + + health = dict_or_empty(progress.get("native_template_health")) + if not health: + return + if health.get("p2p_mining_fresh") is False: + reasons.append(f"{label} native template P2P freshness is false") + fresh_peers = int_or_none(health.get("p2p_fresh_consensus_peer_count")) + if fresh_peers is not None and fresh_peers < min_fresh_peers: + reasons.append( + f"{label} has {fresh_peers} fresh consensus peer(s), below required {min_fresh_peers}" + ) + + sync_progress = dict_or_empty(status.get("sync_progress")) + inspect_progress(sync_progress, "stack") + for node, progress in dict_or_empty(sync_progress.get("nodes")).items(): + if isinstance(progress, dict): + inspect_progress(progress, str(node)) + + pool_health = dict_or_empty(status.get("pool_health", status.get("pool", {}))) + if pool_health.get("source_selected_backend_p2p_fresh") is False: + reasons.append("selected backend native P2P freshness is false") + if pool_health.get("source_selected_backend_submit_ready") is False and pool_health.get("initial_download"): + reason = str(pool_health.get("source_selected_backend_unready_reason") or "").lower() + if "p2p" in reason or "peer" in reason: + reasons.append(f"selected backend is not submit-ready: {reason}") + + deduped: list[str] = [] + for reason in reasons: + if reason and reason not in deduped: + deduped.append(reason) + return "; ".join(deduped) + + def pool_initial_download_effective(status: dict[str, Any]) -> bool: pool_health = status.get("pool_health") if isinstance(status.get("pool_health"), dict) else {} if not pool_health.get("initial_download"): @@ -389,6 +493,83 @@ def pool_has_recent_mining_work(status: dict[str, Any], freshness_seconds: int = return bool(accepted_blocks > 0 and block_age is not None and block_age <= freshness_seconds) +def native_mining_safety_current(status: dict[str, Any]) -> bool: + sync_health = status.get("sync_health") if isinstance(status.get("sync_health"), dict) else {} + if sync_health.get("selected_backend_mining_safe") or sync_health.get("native_chain_progress_safe"): + return True + safety = status.get("canonical_mining_safety") + if isinstance(safety, dict) and safety.get("safe") and not safety.get("public_chain_diverged"): + return True + progress = status.get("sync_progress") if isinstance(status.get("sync_progress"), dict) else {} + remaining = int_or_none(progress.get("remaining_blocks")) + if progress.get("status") == "synced" and (remaining is None or remaining <= 0): + return True + pool_health = status.get("pool_health") if isinstance(status.get("pool_health"), dict) else {} + return bool( + pool_health.get("source_selected_backend_mineable") is True + and pool_health.get("source_selected_backend_submit_ready") is True + and pool_health.get("source_selected_backend_p2p_fresh") is True + ) + + +def active_mining_node_restart_inhibitor(status: dict[str, Any]) -> str: + miner_health = status.get("miner_health") if isinstance(status.get("miner_health"), dict) else {} + connected = int_or_none(miner_health.get("connected_count_effective") or miner_health.get("connected_count")) or 0 + if connected <= 0: + return "" + if not pool_has_recent_mining_work(status, DEFAULT_ACTIVE_MINING_NODE_RESTART_INHIBIT_SECONDS): + paid_state = status.get("sync_health", {}).get("pool_paid_work_state") if isinstance(status.get("sync_health"), dict) else {} + if not ( + isinstance(paid_state, dict) + and int_or_none(paid_state.get("accepted_block_submissions")) is not None + and (int_or_none(paid_state.get("accepted_block_submissions")) or 0) > 0 + and (float_or_none(paid_state.get("last_accepted_age_seconds")) or 999999) + <= DEFAULT_ACTIVE_MINING_NODE_RESTART_INHIBIT_SECONDS + ): + return "" + if not native_mining_safety_current(status): + return "" + return ( + f"{connected} miner(s) connected, accepted block work exists inside " + f"{DEFAULT_ACTIVE_MINING_NODE_RESTART_INHIBIT_SECONDS}s, and native mining safety is current" + ) + + +def update_native_p2p_peer_loss_tracking(state: dict[str, Any], reason: str, now: int) -> int: + if not reason: + reset_native_p2p_peer_loss_tracking(state) + return 0 + + since = int(state.get("native_p2p_peer_loss_since_epoch") or now) + if since <= 0 or since > now: + since = now + state["native_p2p_peer_loss_since_epoch"] = since + state.setdefault("native_p2p_peer_loss_since_at", now_iso()) + state["native_p2p_peer_loss_reason"] = reason + age = max(0, now - since) + state["native_p2p_peer_loss_age_seconds"] = age + return age + + +def reset_native_p2p_peer_loss_tracking(state: dict[str, Any]) -> None: + for key in ( + "native_p2p_peer_loss_since_epoch", + "native_p2p_peer_loss_since_at", + "native_p2p_peer_loss_reason", + "native_p2p_peer_loss_age_seconds", + ): + state.pop(key, None) + + +def youngest_node_started_age_seconds(status: dict[str, Any], now: int) -> int | None: + ages = [ + age + for node in NODES + if (age := container_started_age_seconds(status, node, now)) is not None + ] + return min(ages) if ages else None + + def pool_has_unpaid_template_loss(status: dict[str, Any]) -> bool: pool_health = status.get("pool_health") if isinstance(status.get("pool_health"), dict) else {} miner_health = status.get("miner_health") if isinstance(status.get("miner_health"), dict) else {} @@ -552,6 +733,104 @@ def low_difficulty_primary_miners(status: dict[str, Any]) -> list[dict[str, Any] ) +def active_pool_job_state_macs(status: dict[str, Any]) -> set[str]: + miners = ((status.get("miner_health") or {}).get("miners") or []) + macs = { + str(row.get("mac") or "").strip().lower() + for row in miners + if isinstance(row, dict) + and row.get("pool_job_state_authorized") + and str(row.get("mac") or "").strip() + } + if macs: + return macs + try: + activity = collect_pool_job_state_activity() + except Exception: + return set() + return { + str(row.get("mac") or "").strip().lower() + for row in activity + if isinstance(row, dict) + and row.get("pool_job_state_authorized") + and str(row.get("mac") or "").strip() + } + + +def pool_active_lane_count(status: dict[str, Any], active_macs: set[str]) -> int: + pool_metrics = status.get("pool_metrics") if isinstance(status.get("pool_metrics"), dict) else {} + source_job_health = pool_metrics.get("source_job_health") if isinstance(pool_metrics.get("source_job_health"), dict) else {} + return max( + len(active_macs), + int_or_none(pool_metrics.get("active_connections")) or 0, + int_or_none(pool_metrics.get("authorized_miners")) or 0, + int_or_none(pool_metrics.get("ready_miners")) or 0, + int_or_none(source_job_health.get("authorized_miners")) or 0, + int_or_none(source_job_health.get("ready_miners")) or 0, + ) + + +def probe_missing_asic_api_stall(ip: str) -> str: + errors: list[str] = [] + for label, probe in (("pools", get_miner_pools), ("cgminer-devs", get_miner_cgminer_devs)): + try: + probe(ip, timeout=DEFAULT_ASIC_API_STALL_PROBE_TIMEOUT) + except Exception as exc: # noqa: BLE001 - watchdog records probe evidence, then uses restart policy. + errors.append(f"{label}: {exc}") + issue_text = " ".join(errors).lower() + if errors and any(fragment in issue_text for fragment in ASIC_API_STALL_TEXT_FRAGMENTS): + return "; ".join(errors) + return "" + + +def missing_pool_lane_api_stall_miners(status: dict[str, Any]) -> list[dict[str, Any]]: + mining_address = str(status.get("mining_address") or "") + miners = ((status.get("miner_health") or {}).get("miners") or []) + managed_primary = [ + row + for row in miners + if isinstance(row, dict) + and row.get("managed") + and row.get("device_type") == "asic" + and is_lan_ipv4(str(row.get("ip", ""))) + and is_primary_pool_identity(row, mining_address) + ] + if len(managed_primary) <= 1: + return [] + + active_macs = active_pool_job_state_macs(status) + active_lanes = pool_active_lane_count(status, active_macs) + if active_lanes < max(1, DEFAULT_MINER_USEFUL_WORK_MIN_HEALTHY_PEERS): + return [] + if active_lanes >= len(managed_primary): + return [] + + affected: list[dict[str, Any]] = [] + for row in managed_primary: + mac = str(row.get("mac") or "").strip().lower() + if mac and mac in active_macs: + continue + if row.get("connected") or row.get("pool_active") is True or row.get("work_pool_active") is True: + continue + issue = probe_missing_asic_api_stall(str(row.get("ip"))) + if not issue: + continue + item = dict(row) + item["api_stall_issue"] = issue + item["api_stall_stale_age_seconds"] = ( + int_or_none(row.get("last_pool_seen_age_seconds")) + or int_or_none(row.get("last_share_age_seconds")) + or int_or_none(row.get("last_submit_age_seconds")) + ) + item["api_stall_confirm_seconds"] = DEFAULT_ASIC_MISSING_LANE_CONFIRM_SECONDS + item["pool_job_state_missing_lane"] = True + item["active_pool_lane_count"] = active_lanes + item["expected_pool_lane_count"] = len(managed_primary) + item["restart_open_first"] = True + affected.append(item) + return affected + + def asic_api_stall_primary_miners( status: dict[str, Any], stale_seconds: int = DEFAULT_ASIC_API_STALL_STALE_SECONDS, @@ -622,7 +901,15 @@ def asic_api_stall_primary_miners( item["api_stall_stale_age_seconds"] = stale_age item["restart_open_first"] = True affected.append(item) - return affected + by_identity: dict[str, dict[str, Any]] = {} + for item in [*affected, *missing_pool_lane_api_stall_miners(status)]: + key = miner_stall_identity_key(item) + if not key: + continue + existing = by_identity.get(key) + if existing is None or item.get("pool_job_state_missing_lane"): + by_identity[key] = item + return list(by_identity.values()) def useful_work_stalled_primary_miners( @@ -1459,6 +1746,26 @@ def suppress_sync_restart_for_active_import( return False if target_node and target_node not in active_nodes: return False + peer_loss_reason = native_p2p_peer_loss_reason(status) + if peer_loss_reason: + state["last_sync_repair_suppression_bypassed_epoch"] = int(time.time()) + state["last_sync_repair_suppression_bypassed_at"] = now_iso() + state["last_sync_repair_suppression_bypassed_reason"] = peer_loss_reason + log( + "sync restart suppression bypassed because native P2P is not healthy " + f"target={target_node or 'stack'} active_nodes={','.join(active_nodes)} reason={peer_loss_reason}" + ) + record_efficiency_event( + "repair_suppression_bypassed", + "warning", + "active import did not suppress repair because native P2P is not healthy", + { + "active_nodes": active_nodes, + "target_node": target_node, + "reason": peer_loss_reason, + }, + ) + return False pool_health = status.get("pool_health", status.get("pool", {})) sync_health = status.get("sync_health", {}) if isinstance(status.get("sync_health"), dict) else {} @@ -1717,7 +2024,7 @@ def check_once( repair: bool = True, ) -> dict[str, Any]: state = read_state() - status = collect_stack_status(include_logs=True) + status = normalize_watchdog_status_payload(collect_stack_status(include_logs=True)) stack_failures = status.get("stack_failures", status["failures"]) miner_failures = status.get("miner_failures", []) failures = stack_failures + miner_failures @@ -1740,6 +2047,18 @@ def check_once( ) now = int(time.time()) observe_sync_progress(status, state, now) + native_peer_loss_reason = native_p2p_peer_loss_reason(status) + native_peer_loss_age = update_native_p2p_peer_loss_tracking(state, native_peer_loss_reason, now) + node_started_age_seconds = youngest_node_started_age_seconds(status, now) + if ( + native_peer_loss_reason + and node_started_age_seconds is not None + and native_peer_loss_age > node_started_age_seconds + 5 + ): + reset_native_p2p_peer_loss_tracking(state) + state["native_p2p_peer_loss_reset_reason"] = "peer-loss timer predates current node start" + state["native_p2p_peer_loss_reset_at"] = now_iso() + native_peer_loss_age = update_native_p2p_peer_loss_tracking(state, native_peer_loss_reason, now) pool_started_age_seconds = container_started_age_seconds(status, POOL_CONTAINER, now) pool_in_startup_grace = bool( pool_started_age_seconds is not None @@ -1916,7 +2235,7 @@ def check_once( return {"status": status, "watchdog_state": state} sync_pause_reason = sync_progress_pool_pause_reason(status) - if sync_pause_reason and container_running(status, POOL_CONTAINER): + if sync_pause_reason and container_running(status, POOL_CONTAINER) and not native_peer_loss_reason: state["consecutive_failures"] = 0 state["consecutive_syncing"] = 0 state["consecutive_share_stalls"] = 0 @@ -2156,15 +2475,16 @@ def check_once( ip = str(item.get("ip")) identity_key = miner_stall_identity_key(item) stalled_for = now - int(asic_api_stall_since.get(identity_key, now) or now) + confirm_seconds = int_or_none(item.get("api_stall_confirm_seconds")) or DEFAULT_ASIC_API_STALL_CONFIRM_SECONDS cooldown_remaining = DEFAULT_ASIC_API_STALL_REPAIR_COOLDOWN - ( now - int(miner_restart_by_ip.get(ip, 0) or 0) ) - if stalled_for >= DEFAULT_ASIC_API_STALL_CONFIRM_SECONDS and cooldown_remaining <= 0: + if stalled_for >= confirm_seconds and cooldown_remaining <= 0: eligible_miners.append(item) else: waiting.append( f"{identity_key or ip} ip={ip} stalled_for={stalled_for}s " - f"confirm={DEFAULT_ASIC_API_STALL_CONFIRM_SECONDS}s " + f"confirm={confirm_seconds}s " f"cooldown_remaining={max(cooldown_remaining, 0)}s" ) reason = ( @@ -2852,16 +3172,23 @@ def check_once( state["last_share_repair_at"] = int(time.time()) if ok: state["consecutive_share_stalls"] = 0 - elif status.get("sync_health", {}).get("needs_fast_sync_repair"): - sync_warnings = status.get("sync_warnings", status.get("warnings", [])) + elif status.get("sync_health", {}).get("needs_fast_sync_repair") or native_peer_loss_reason: + sync_warnings = list_or_empty(status.get("sync_warnings", status.get("warnings", []))) + if native_peer_loss_reason and native_peer_loss_reason not in sync_warnings: + sync_warnings.append(native_peer_loss_reason) recent_mining_work = pool_has_recent_mining_work(status) + native_peer_loss_repair_ready = bool( + native_peer_loss_reason + and native_peer_loss_age >= DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS + and not recent_mining_work + ) state["consecutive_failures"] = 0 - if recent_mining_work: + if recent_mining_work and not native_peer_loss_reason: state["consecutive_syncing"] = 0 else: state["consecutive_syncing"] = int(state.get("consecutive_syncing", 0) or 0) + 1 state["consecutive_share_stalls"] = 0 - state["last_status"] = "syncing" + state["last_status"] = "p2p_down" if native_peer_loss_reason else "syncing" state["last_failures"] = [] state["last_sync_warnings"] = sync_warnings log( @@ -2876,9 +3203,11 @@ def check_once( { "consecutive_syncing": state["consecutive_syncing"], "recent_mining_work": recent_mining_work, + "native_peer_loss_age_seconds": native_peer_loss_age if native_peer_loss_reason else 0, + "native_peer_loss_repair_seconds": DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS, }, ) - if repair and recent_mining_work: + if repair and recent_mining_work and not native_peer_loss_reason: log("sync repair suppressed because paid block submission is fresh") record_efficiency_event( "repair_suppressed", @@ -2889,9 +3218,47 @@ def check_once( "freshness_seconds": 60, }, ) - if repair and state["consecutive_syncing"] and should_restart_for_syncing(state, syncing_threshold, syncing_restart_cooldown): + elif repair and native_peer_loss_reason and not native_peer_loss_repair_ready: + log( + "native P2P repair suppressed until peer loss is sustained " + f"age={native_peer_loss_age}s threshold={DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS}s " + f"recent_mining_work={recent_mining_work}" + ) + record_efficiency_event( + "repair_suppressed", + "warning", + "native P2P repair suppressed until peer loss is sustained", + { + "native_peer_loss_reason": native_peer_loss_reason, + "native_peer_loss_age_seconds": native_peer_loss_age, + "native_peer_loss_repair_seconds": DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS, + "recent_mining_work": recent_mining_work, + }, + ) + if ( + repair + and state["consecutive_syncing"] + and (not native_peer_loss_reason or native_peer_loss_repair_ready) + and should_restart_for_syncing(state, syncing_threshold, syncing_restart_cooldown) + ): restart_node = template_nodes[0] if template_nodes else choose_lagging_node(status) - if suppress_sync_restart_for_active_import(status, state, "; ".join(sync_warnings), restart_node): + active_mining_inhibitor = active_mining_node_restart_inhibitor(status) + if active_mining_inhibitor and not native_peer_loss_reason: + ok = False + repair_attempted = False + state["last_sync_repair_suppressed_epoch"] = int(time.time()) + state["last_sync_repair_suppressed_reason"] = active_mining_inhibitor + log(f"sync repair suppressed because active mining would be interrupted: {active_mining_inhibitor}") + record_efficiency_event( + "repair_suppressed", + "warning", + "node restart suppressed because active mining is still safe", + { + "sync_warnings": sync_warnings, + "active_mining_inhibitor": active_mining_inhibitor, + }, + ) + elif suppress_sync_restart_for_active_import(status, state, "; ".join(sync_warnings), restart_node): ok = False repair_attempted = False elif restart_node: @@ -2938,6 +3305,7 @@ def loop( "watchdog started " f"interval={interval}s threshold={threshold} clean_restore_cooldown={clean_restore_cooldown}s " f"syncing_threshold={syncing_threshold} syncing_restart_cooldown={syncing_restart_cooldown}s " + f"native_p2p_peer_loss_repair={DEFAULT_NATIVE_P2P_PEER_LOSS_REPAIR_SECONDS}s " f"miner_down_restart_seconds={miner_down_restart_seconds}s miner_restart_cooldown={miner_restart_cooldown}s " f"miner_useful_work_stall_seconds={DEFAULT_MINER_USEFUL_WORK_STALL_SECONDS}s " f"miner_useful_work_confirm={DEFAULT_MINER_USEFUL_WORK_STALL_CONFIRM_SECONDS}s " diff --git a/scripts/mining-appliance-preflight.py b/scripts/mining-appliance-preflight.py index a358eef..cc34ded 100644 --- a/scripts/mining-appliance-preflight.py +++ b/scripts/mining-appliance-preflight.py @@ -778,6 +778,8 @@ def check_node_data_layout(checks: list[Check], root: Path, env: dict[str, str]) def check_env_defaults(checks: list[Check], env: dict[str, str], profile: HostProfile) -> None: evidence = { "BDAG_NODE_CACHE_MB": env.get("BDAG_NODE_CACHE_MB"), + "BDAG_NODE_DEBUG_LEVEL": env.get("BDAG_NODE_DEBUG_LEVEL"), + "BDAG_NODE_NO_FILE_LOGGING": env.get("BDAG_NODE_NO_FILE_LOGGING"), "NODE_MAX_PEERS": env.get("NODE_MAX_PEERS"), "SYNC_SOURCE_NODE": env.get("SYNC_SOURCE_NODE"), "BDAG_STORAGE_PROFILE": env.get("BDAG_STORAGE_PROFILE"), @@ -812,6 +814,15 @@ def check_env_defaults(checks: list[Check], env: dict[str, str], profile: HostPr else: add(checks, "pass", "peer_budget", f"NODE_MAX_PEERS={max_peers}", evidence=evidence) + debug_level = (env.get("BDAG_NODE_DEBUG_LEVEL") or "warn").strip().lower() + file_logging = (env.get("BDAG_NODE_NO_FILE_LOGGING") or "1").strip().lower() + if debug_level in {"trace", "debug", "info"}: + add(checks, "warn", "node_log_budget", f"BDAG_NODE_DEBUG_LEVEL={debug_level} emits high-volume import logs.", "Use warn or error on mining appliances; status-sampler tracks catch-up progress without per-block node logs.", evidence) + elif file_logging in {"0", "false", "no", "off", "disabled"}: + add(checks, "warn", "node_log_budget", "node file logging is enabled.", "Use BDAG_NODE_NO_FILE_LOGGING=1 so catch-up writes do not also hit /var/log/bdagStack.", evidence) + else: + add(checks, "pass", "node_log_budget", f"BDAG_NODE_DEBUG_LEVEL={debug_level}; file logging disabled", evidence=evidence) + storage_profile = (env.get("BDAG_STORAGE_PROFILE") or "").strip().lower() topology = (env.get("BDAG_DETECTED_NETWORK_TOPOLOGY") or env.get("BDAG_NETWORK_TOPOLOGY") or "").strip().lower() constrained_mining_profile = ( diff --git a/scripts/release-readiness-check.py b/scripts/release-readiness-check.py index 0e1adf5..6b703b2 100755 --- a/scripts/release-readiness-check.py +++ b/scripts/release-readiness-check.py @@ -29,6 +29,8 @@ ROOT = Path(__file__).resolve().parents[1] DEFAULT_RPC_URL = "http://127.0.0.1:38131" DEFAULT_SCHEMA_FILE = ROOT / "sql" / "pool-schema.sql" +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" +ETH_ADDRESS_RE = re.compile(r"^0x[0-9a-fA-F]{40}$") REQUIRED_SCHEMA = { "miners": {"address", "joined_at", "last_active"}, "blocks": {"hash", "height", "reward", "fees", "status", "created_at"}, @@ -50,6 +52,19 @@ "accepted", "outcome", "message", + "worker", + "job_id", + "client_addr", + "remote_host", + "asic_mac", + "lane_id", + "lane_identity_source", + "protocol_variant", + "extranonce_subscribed", + "backend_attempted", + "pdiff", + "share_diff", + "job_age_ms", "created_at", }, "payouts": {"id", "tx_hash", "amount", "created_at"}, @@ -70,6 +85,21 @@ "columns": ("outcome", "created_at"), "unique": False, }, + "block_submissions_lane_created_idx": { + "table": "block_submissions", + "columns": ("lane_id", "created_at"), + "unique": False, + }, + "block_submissions_lane_outcome_created_idx": { + "table": "block_submissions", + "columns": ("lane_id", "outcome", "created_at"), + "unique": False, + }, + "block_submissions_asic_created_idx": { + "table": "block_submissions", + "columns": ("asic_mac", "created_at"), + "unique": False, + }, } @@ -155,6 +185,33 @@ def method_not_found(err: RPCError) -> bool: return err.code == -32601 or "method not found" in err.message.lower() +def postgres_service_candidates(args: argparse.Namespace, env: dict[str, str]) -> list[str]: + explicit = str(args.postgres_service or "").strip() + if explicit: + return [explicit] + candidates = [ + env.get("BDAG_POSTGRES_SERVICE"), + env.get("BDAG_POOL_DB_SERVICE"), + "pool-db", + "postgres", + ] + result: list[str] = [] + for item in candidates: + service = str(item or "").strip() + if service and service not in result: + result.append(service) + return result + + +def postgres_service_lookup_failed(stderr: str) -> bool: + lowered = stderr.lower() + return ( + "no such service" in lowered + or "is not running" in lowered + or "service " in lowered and "not running" in lowered + ) + + def check_postgres_schema(args: argparse.Namespace, env: dict[str, str]) -> CheckResult: if args.skip_postgres: return CheckResult("postgres_schema", True, "skipped by request", skipped=True) @@ -174,7 +231,14 @@ def check_postgres_schema(args: argparse.Namespace, env: dict[str, str]) -> Chec ('block_submissions','node_block_hash'), ('block_submissions','height'), ('block_submissions','backend'), ('block_submissions','template_seq'), ('block_submissions','accepted'), ('block_submissions','outcome'), - ('block_submissions','message'), ('block_submissions','created_at'), + ('block_submissions','message'), ('block_submissions','worker'), + ('block_submissions','job_id'), ('block_submissions','client_addr'), + ('block_submissions','remote_host'), ('block_submissions','asic_mac'), + ('block_submissions','lane_id'), ('block_submissions','lane_identity_source'), + ('block_submissions','protocol_variant'), ('block_submissions','extranonce_subscribed'), + ('block_submissions','backend_attempted'), ('block_submissions','pdiff'), + ('block_submissions','share_diff'), ('block_submissions','job_age_ms'), + ('block_submissions','created_at'), ('payouts','id'), ('payouts','tx_hash'), ('payouts','amount'), ('payouts','created_at') ) SELECT table_name || '.' || column_name @@ -206,7 +270,10 @@ def check_postgres_schema(args: argparse.Namespace, env: dict[str, str]) -> Chec FROM ( VALUES ('block_submissions_created_at_idx', 'block_submissions', '%(created_at)%'), - ('block_submissions_outcome_created_idx', 'block_submissions', '%(outcome, created_at)%') + ('block_submissions_outcome_created_idx', 'block_submissions', '%(outcome, created_at)%'), + ('block_submissions_lane_created_idx', 'block_submissions', '%(lane_id, created_at)%'), + ('block_submissions_lane_outcome_created_idx', 'block_submissions', '%(lane_id, outcome, created_at)%'), + ('block_submissions_asic_created_idx', 'block_submissions', '%(asic_mac, created_at)%') ) AS r(index_name, table_name, column_pattern) WHERE NOT EXISTS ( SELECT 1 @@ -225,37 +292,53 @@ def check_postgres_schema(args: argparse.Namespace, env: dict[str, str]) -> Chec if args.pg_url: cmd = ["psql", args.pg_url, "-v", "ON_ERROR_STOP=1", "-Atc", query] run_env = os.environ.copy() + proc = subprocess.run( + cmd, + cwd=ROOT, + env=run_env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) else: - cmd = [ - "docker", - "compose", - "exec", - "-T", - "-e", - f"PGPASSWORD={postgres_password}", - args.postgres_service, - "psql", - "-v", - "ON_ERROR_STOP=1", - "-U", - postgres_user, - "-d", - postgres_db, - "-Atc", - query, - ] run_env = os.environ.copy() run_env.update(env) - - proc = subprocess.run( - cmd, - cwd=ROOT, - env=run_env, - text=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=False, - ) + proc = None + candidates = postgres_service_candidates(args, env) + for index, service in enumerate(candidates): + cmd = [ + "docker", + "compose", + "exec", + "-T", + "-e", + f"PGPASSWORD={postgres_password}", + service, + "psql", + "-v", + "ON_ERROR_STOP=1", + "-U", + postgres_user, + "-d", + postgres_db, + "-Atc", + query, + ] + proc = subprocess.run( + cmd, + cwd=ROOT, + env=run_env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) + if proc.returncode == 0: + break + if index + 1 >= len(candidates) or not postgres_service_lookup_failed(proc.stderr): + break + assert proc is not None if proc.returncode != 0: stderr = proc.stderr.replace(postgres_password, "[redacted]") raise CheckError(f"Postgres schema query failed: {stderr.strip()}") @@ -287,8 +370,6 @@ def check_sync_or_mineable(args: argparse.Namespace) -> CheckResult: if health.get("last_template_build_error_blocking") is True: code = health.get("last_template_build_error_code") or "template_build_error" blocking_reasons.append(f"blocking template build error: {code}") - if health.get("submit_ready") is False: - blocking_reasons.append("submit_ready=false") if health.get("get_block_template_ready") is False: reason = health.get("get_block_template_reason_code") or "unknown" blocking_reasons.append(f"get_block_template_ready=false:{reason}") @@ -302,8 +383,16 @@ def check_sync_or_mineable(args: argparse.Namespace) -> CheckResult: "; ".join(blocking_reasons), ) mineable = bool(health.get("mineable_now") or health.get("submit_ready")) + template_ready = health.get("get_block_template_ready") is not False + p2p_fresh = health.get("p2p_mining_fresh") is not False sync_allowed = bool(health.get("sync_allowed")) chain_current = bool(health.get("chain_current") or health.get("p2p_current")) + if health.get("submit_ready") is False and mineable and template_ready and p2p_fresh: + return CheckResult( + "node_mineable_or_synced", + True, + "template health is mineable with transient submit_ready=false; P2P/template proof is safe", + ) if mineable: return CheckResult( "node_mineable_or_synced", @@ -409,9 +498,14 @@ def check_peer_sanity(args: argparse.Namespace, node_info: dict[str, Any]) -> Ch def check_get_block_template(args: argparse.Namespace) -> CheckResult: - params: list[Any] = [[], args.pow_type] - if args.mining_address: - params.append(args.mining_address) + expected_address = normalize_eth_address(args.mining_address) + if not expected_address: + return CheckResult( + "get_block_template", + False, + "missing non-zero expected mining address", + ) + params: list[Any] = [[], args.pow_type, expected_address] template = rpc_call( args.rpc_url, args.rpc_user, @@ -440,13 +534,53 @@ def check_get_block_template(args: argparse.Namespace) -> CheckResult: False, "template missing " + ", ".join(sorted(set(missing))), ) + coinbase = normalize_eth_address(str(template.get("coinbase_address") or "")) + if not coinbase: + return CheckResult( + "get_block_template", + False, + f"template coinbase is invalid or zero: {template.get('coinbase_address')!r}", + ) + if coinbase != expected_address: + return CheckResult( + "get_block_template", + False, + f"template coinbase {coinbase} does not match expected {expected_address}", + ) return CheckResult( "get_block_template", True, - f"height={template.get('height')} parent={str(template.get('previousblockhash'))[:16]}...", + ( + f"height={template.get('height')} " + f"parent={str(template.get('previousblockhash'))[:16]}... " + f"coinbase={coinbase}" + ), ) +def normalize_eth_address(value: Any) -> str: + text = str(value or "").strip() + if not ETH_ADDRESS_RE.fullmatch(text): + return "" + normalized = "0x" + text[2:].lower() + if normalized == ZERO_ADDRESS: + return "" + return normalized + + +def expected_mining_address(args: argparse.Namespace, env: dict[str, str]) -> str: + for value in ( + args.mining_address, + env.get("POOL_COINBASE_ADDRESS"), + env.get("MINING_POOL_ADDRESS"), + env.get("MINING_ADDRESS"), + ): + normalized = normalize_eth_address(value) + if normalized: + return normalized + return "" + + def check_mining_rpc_stability( args: argparse.Namespace, node_info: dict[str, Any] ) -> CheckResult: @@ -501,7 +635,7 @@ def run_checks(args: argparse.Namespace) -> list[CheckResult]: args.rpc_url = args.rpc_url or env.get("BDAG_RPC_URL") or DEFAULT_RPC_URL args.rpc_user = args.rpc_user if args.rpc_user is not None else env.get("NODE_RPC_USER", "test") args.rpc_pass = args.rpc_pass if args.rpc_pass is not None else env.get("NODE_RPC_PASS", "test") - args.mining_address = args.mining_address or env.get("MINING_POOL_ADDRESS", "") + args.mining_address = expected_mining_address(args, env) args.schema_file = args.schema_file or str(DEFAULT_SCHEMA_FILE) results: list[CheckResult] = [] @@ -552,7 +686,11 @@ def build_parser() -> argparse.ArgumentParser: parser.add_argument("--pow-type", type=int, default=10) parser.add_argument("--mining-address", default=None) parser.add_argument("--skip-postgres", action="store_true") - parser.add_argument("--postgres-service", default="postgres") + parser.add_argument( + "--postgres-service", + default=None, + help="Compose service for Postgres; defaults to pool-db with postgres fallback.", + ) parser.add_argument("--pg-url", default=None, help="Optional direct psql URL.") parser.add_argument("--schema-file", default=None) parser.add_argument("--json", action="store_true", help="Emit JSON summary.") diff --git a/scripts/release/installers/install-unix-common.sh b/scripts/release/installers/install-unix-common.sh index 6836381..f026009 100755 --- a/scripts/release/installers/install-unix-common.sh +++ b/scripts/release/installers/install-unix-common.sh @@ -413,15 +413,30 @@ set_env_value() { local file="$1" local key="$2" local value="$3" - local escaped - escaped="$(sed_escape "$value")" + local line escaped + line="${key}=$(quote_env_value "$value")" + escaped="$(sed_escape "$line")" if grep -q "^${key}=" "$file"; then - inplace_sed "s|^${key}=.*|${key}=${escaped}|" "$file" + inplace_sed "s|^${key}=.*|${escaped}|" "$file" else - printf '\n%s=%s\n' "$key" "$value" >> "$file" + printf '\n%s\n' "$line" >> "$file" fi } +quote_env_value() { + local value="$1" + case "$value" in + *[!A-Za-z0-9_./:@,%+=-]*) + printf '"' + printf '%s' "$value" | sed 's/[\\$"`]/\\&/g' + printf '"' + ;; + *) + printf '%s' "$value" + ;; + esac +} + env_file_value() { local file="$1" key="$2" value value="$(grep -E "^${key}=" "$file" 2>/dev/null | tail -n 1 | cut -d= -f2- || true)" diff --git a/scripts/release_bootstrap_static_test.py b/scripts/release_bootstrap_static_test.py index 2948904..e258dcd 100755 --- a/scripts/release_bootstrap_static_test.py +++ b/scripts/release_bootstrap_static_test.py @@ -110,13 +110,13 @@ def test_release_defaults_pass_bootstrap_peers_to_node(self) -> None: self.assertIn("BOOTSTRAP_PEER_ADDRESSES: ${BOOTSTRAP_PEER_ADDRESSES:-}", compose) self.assertIn(f"addpeer={self.LIVE_PUBLIC_BOOTSTRAP_PEER}", node_conf) - def test_release_defaults_keep_compose_and_node_config_on_p2p_port_8150(self) -> None: + def test_release_defaults_keep_compose_on_safe_local_p2p_port_8155(self) -> None: env_example = (ROOT / ".env.example").read_text(encoding="utf-8") compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8") node_conf = (ROOT / "node.conf.example").read_text(encoding="utf-8") - self.assertRegex(env_example, r"(?m)^P2P_PORT=8150$") - self.assertIn("P2P_PORT: ${P2P_PORT:-8150}", compose) + self.assertRegex(env_example, r"(?m)^P2P_PORT=8155$") + self.assertIn("P2P_PORT: ${P2P_PORT:-8155}", compose) self.assertRegex(node_conf, r"(?m)^port=8150$") self.assertNotRegex(node_conf, r"(?m)^port=8154$") diff --git a/scripts/release_readiness_check_test.py b/scripts/release_readiness_check_test.py index c40285a..8c8a6f2 100755 --- a/scripts/release_readiness_check_test.py +++ b/scripts/release_readiness_check_test.py @@ -19,6 +19,10 @@ sys.modules["release_readiness_check"] = readiness SPEC.loader.exec_module(readiness) +GOOD_ADDRESS = "0x1234567890abcdef1234567890abcdef12345678" +OTHER_ADDRESS = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" + class MockRPCHandler(BaseHTTPRequestHandler): def do_POST(self) -> None: @@ -63,7 +67,7 @@ def do_POST(self) -> None: "previousblockhash": "abcd", "txroot": "tx", "stateroot": "state", - "coinbase_address": "0x0000000000000000000000000000000000000000", + "coinbase_address": GOOD_ADDRESS, "pow_diff_reference": {"nbits": "1d00ffff"}, }, }[method] @@ -102,9 +106,9 @@ def args(self) -> argparse.Namespace: stability_samples=1, stability_interval=0.0, pow_type=10, - mining_address="", + mining_address=GOOD_ADDRESS, skip_postgres=True, - postgres_service="postgres", + postgres_service=None, pg_url=None, schema_file=None, json=False, @@ -116,6 +120,94 @@ def test_readiness_passes_with_sane_peer_and_template(self) -> None: peer_result = next(result for result in results if result.name == "peer_sanity") self.assertIn("1 sane peers", peer_result.detail) + def test_get_block_template_passes_expected_coinbase_to_rpc(self) -> None: + args = self.args() + calls = [] + + def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): + calls.append((method, params)) + if method == "getBlockTemplate": + return { + "height": 42, + "previousblockhash": "abcd", + "txroot": "tx", + "stateroot": "state", + "coinbase_address": GOOD_ADDRESS.upper().replace("X", "x", 1), + "pow_diff_reference": {"nbits": "1d00ffff"}, + } + raise AssertionError(method) + + with mock.patch.object(readiness, "rpc_call", side_effect=fake_rpc_call): + result = readiness.check_get_block_template(args) + + self.assertTrue(result.ok) + self.assertEqual(calls, [("getBlockTemplate", [[], 10, GOOD_ADDRESS])]) + self.assertIn(f"coinbase={GOOD_ADDRESS}", result.detail) + + def test_get_block_template_rejects_missing_expected_address(self) -> None: + args = self.args() + args.mining_address = "" + + with mock.patch.object(readiness, "rpc_call") as rpc_call: + result = readiness.check_get_block_template(args) + + self.assertFalse(result.ok) + self.assertIn("missing non-zero expected mining address", result.detail) + rpc_call.assert_not_called() + + def test_get_block_template_rejects_zero_coinbase(self) -> None: + args = self.args() + + def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): + if method == "getBlockTemplate": + return { + "height": 42, + "previousblockhash": "abcd", + "txroot": "tx", + "stateroot": "state", + "coinbase_address": ZERO_ADDRESS, + "pow_diff_reference": {"nbits": "1d00ffff"}, + } + raise AssertionError(method) + + with mock.patch.object(readiness, "rpc_call", side_effect=fake_rpc_call): + result = readiness.check_get_block_template(args) + + self.assertFalse(result.ok) + self.assertIn("template coinbase is invalid or zero", result.detail) + + def test_get_block_template_rejects_coinbase_mismatch(self) -> None: + args = self.args() + + def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): + if method == "getBlockTemplate": + return { + "height": 42, + "previousblockhash": "abcd", + "txroot": "tx", + "stateroot": "state", + "coinbase_address": OTHER_ADDRESS, + "pow_diff_reference": {"nbits": "1d00ffff"}, + } + raise AssertionError(method) + + with mock.patch.object(readiness, "rpc_call", side_effect=fake_rpc_call): + result = readiness.check_get_block_template(args) + + self.assertFalse(result.ok) + self.assertIn(f"template coinbase {OTHER_ADDRESS}", result.detail) + self.assertIn(f"expected {GOOD_ADDRESS}", result.detail) + + def test_expected_mining_address_precedence(self) -> None: + args = self.args() + args.mining_address = "" + env = { + "MINING_POOL_ADDRESS": OTHER_ADDRESS, + "POOL_COINBASE_ADDRESS": GOOD_ADDRESS.upper().replace("X", "x", 1), + } + + self.assertEqual(readiness.expected_mining_address(args, env), GOOD_ADDRESS) + def test_peer_gate_fails_when_minimum_exceeds_filtered_peers(self) -> None: args = self.args() args.min_peers = 2 @@ -165,7 +257,7 @@ def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): "previousblockhash": "abcd", "txroot": "tx", "stateroot": "state", - "coinbase_address": "0x0000000000000000000000000000000000000000", + "coinbase_address": GOOD_ADDRESS, "pow_diff_reference": {"nbits": "1d00ffff"}, } raise AssertionError(method) @@ -177,7 +269,7 @@ def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): self.assertTrue(node_result.skipped) self.assertTrue(all(result.ok for result in results), results) - def test_template_health_gate_rejects_submit_not_ready(self) -> None: + def test_template_health_gate_allows_submit_ready_flicker_when_template_and_p2p_are_safe(self) -> None: args = self.args() with mock.patch.object( readiness, @@ -190,9 +282,27 @@ def test_template_health_gate_rejects_submit_not_ready(self) -> None: }, ): result = readiness.check_sync_or_mineable(args) - self.assertFalse(result.ok) + self.assertTrue(result.ok) self.assertIn("submit_ready=false", result.detail) + def test_template_health_gate_rejects_submit_not_ready_without_mineable_template(self) -> None: + args = self.args() + with mock.patch.object( + readiness, + "rpc_call", + return_value={ + "mineable_now": False, + "submit_ready": False, + "p2p_mining_fresh": True, + "get_block_template_ready": True, + "sync_allowed": False, + "chain_current": False, + }, + ): + result = readiness.check_sync_or_mineable(args) + self.assertFalse(result.ok) + self.assertIn("not mineable/current enough", result.detail) + def test_template_health_gate_rejects_stale_p2p(self) -> None: args = self.args() with mock.patch.object( @@ -262,7 +372,7 @@ def fake_rpc_call(url, user, password, method, params=None, timeout=5.0): "previousblockhash": "abcd", "txroot": "tx", "stateroot": "state", - "coinbase_address": "0x0000000000000000000000000000000000000000", + "coinbase_address": GOOD_ADDRESS, "pow_diff_reference": {"nbits": "1d00ffff"}, } raise AssertionError(method) @@ -295,7 +405,13 @@ def test_postgres_schema_requires_block_submission_history(self) -> None: proc = mock.Mock( returncode=0, - stdout="block_submissions.candidate_hash\nindex:block_submissions_created_at_idx\n", + stdout=( + "block_submissions.candidate_hash\n" + "block_submissions.asic_mac\n" + "block_submissions.lane_id\n" + "index:block_submissions_created_at_idx\n" + "index:block_submissions_lane_created_idx\n" + ), stderr="", ) with mock.patch.object(readiness.subprocess, "run", return_value=proc) as run: @@ -303,10 +419,17 @@ def test_postgres_schema_requires_block_submission_history(self) -> None: self.assertFalse(result.ok) self.assertIn("block_submissions.candidate_hash", result.detail) + self.assertIn("block_submissions.asic_mac", result.detail) + self.assertIn("block_submissions.lane_id", result.detail) self.assertIn("index:block_submissions_created_at_idx", result.detail) + self.assertIn("index:block_submissions_lane_created_idx", result.detail) query = run.call_args.args[0][-1] self.assertIn("block_submissions", query) + self.assertIn("('block_submissions','asic_mac')", query) + self.assertIn("('block_submissions','lane_id')", query) self.assertIn("block_submissions_outcome_created_idx", query) + self.assertIn("block_submissions_lane_outcome_created_idx", query) + self.assertIn("block_submissions_asic_created_idx", query) def test_postgres_schema_passes_with_credit_unique_index(self) -> None: args = self.args() @@ -320,6 +443,48 @@ def test_postgres_schema_passes_with_credit_unique_index(self) -> None: self.assertTrue(result.ok) self.assertIn("required indexes present", result.detail) + def test_postgres_schema_defaults_to_pool_db_compose_service(self) -> None: + args = self.args() + args.skip_postgres = False + + proc = mock.Mock(returncode=0, stdout="", stderr="") + with mock.patch.object(readiness.subprocess, "run", return_value=proc) as run: + result = readiness.check_postgres_schema(args, {}) + + self.assertTrue(result.ok) + command = run.call_args.args[0] + self.assertIn("pool-db", command) + self.assertNotIn("postgres", command[: command.index("psql")]) + + def test_postgres_schema_falls_back_to_legacy_postgres_service(self) -> None: + args = self.args() + args.skip_postgres = False + + failed = mock.Mock( + returncode=1, + stdout="", + stderr='service "pool-db" is not running', + ) + passed = mock.Mock(returncode=0, stdout="", stderr="") + with mock.patch.object(readiness.subprocess, "run", side_effect=[failed, passed]) as run: + result = readiness.check_postgres_schema(args, {}) + + self.assertTrue(result.ok) + self.assertEqual(run.call_count, 2) + self.assertIn("pool-db", run.call_args_list[0].args[0]) + self.assertIn("postgres", run.call_args_list[1].args[0]) + + def test_postgres_schema_does_not_fallback_on_query_failure(self) -> None: + args = self.args() + args.skip_postgres = False + + failed = mock.Mock(returncode=1, stdout="", stderr="ERROR: relation miners does not exist") + with mock.patch.object(readiness.subprocess, "run", return_value=failed) as run: + with self.assertRaises(readiness.CheckError): + readiness.check_postgres_schema(args, {}) + + self.assertEqual(run.call_count, 1) + if __name__ == "__main__": unittest.main() diff --git a/scripts/validate-pi5-restart-hardening.sh b/scripts/validate-pi5-restart-hardening.sh index 7ec53c9..feb3169 100755 --- a/scripts/validate-pi5-restart-hardening.sh +++ b/scripts/validate-pi5-restart-hardening.sh @@ -243,26 +243,39 @@ need_grep 'BDAG_BACKGROUND_MAINTENANCE_IO_SOME_AVG10_WARN=' ".env.example" need_grep 'BDAG_BACKGROUND_MAINTENANCE_CPU_SOME_AVG10_WARN=' ".env.example" need_grep 'BDAG_BACKGROUND_MAINTENANCE_CHAIN_RPC_WARN_MS=' ".env.example" need_grep 'BDAG_GLOBAL_HISTORY_COMPACT_MULTIPLIER=' ".env.example" -need_grep 'BDAG_ENTRYPOINT_CHOWN_MODE=' ".env.example" -need_grep 'BDAG_NODEWORKER_LIVENESS_TIMEOUT=' ".env.example" -need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=' ".env.example" -need_grep 'POOL_SUBMIT_STALE_BLOCK_CANDIDATES=' ".env.example" -need_grep 'POOL_TEMPLATE_TTL_REFRESH_MS=' ".env.example" -need_grep 'POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=' ".env.example" -need_grep 'POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=' ".env.example" + need_grep 'BDAG_ENTRYPOINT_CHOWN_MODE=' ".env.example" + need_grep 'BDAG_NODEWORKER_LIVENESS_TIMEOUT=' ".env.example" + need_grep '^NODE_DATA_DIR=./data/node$' ".env.example" + need_grep '^POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp$' ".env.example" + need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_ENABLED=' ".env.example" + need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_FRESH_TEMPLATE_GRACE_SECONDS=' ".env.example" + need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=' ".env.example" + need_grep 'POOL_SUBMIT_STALE_BLOCK_CANDIDATES=' ".env.example" + need_grep 'POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=' ".env.example" + need_grep 'POOL_TEMPLATE_TTL_REFRESH_MS=' ".env.example" + need_grep 'POOL_MAX_BLOCK_CANDIDATE_JOB_AGE_MS=' ".env.example" + need_grep 'POOL_AUTO_TUNE_BLOCK_CANDIDATE_JOB_AGE=' ".env.example" + need_grep 'POOL_AUTO_TUNE_BLOCK_CANDIDATE_MAX_AGE_MS=' ".env.example" + need_grep 'POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED=' ".env.example" need_grep 'POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD=' ".env.example" need_grep 'POOL_STALE_RACE_RECOVERY_COOLDOWN_SECONDS=' ".env.example" need_grep 'POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD=' ".env.example" need_grep 'POOL_EXPIRED_JOB_CLIENT_RECONNECT_WINDOW_SECONDS=' ".env.example" -need_grep 'POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=' ".env.example" -need_grep 'POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=' ".env.example" -need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=' ".env.example" + need_grep 'POOL_EXPIRED_JOB_CLIENT_RECONNECT_COOLDOWN_SECONDS=' ".env.example" + need_grep 'POOL_ALLOW_MULTIPLE_BLOCK_CANDIDATES_PER_JOB=' ".env.example" + need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_ENABLED=' ".env.example" + need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_CLEAN_REISSUE_ENABLED=' ".env.example" + need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_DELAY_MS=' ".env.example" + need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=' ".env.example" + need_grep 'POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_TIMEOUT_MS=' ".env.example" need_grep 'NODE_RPC_URLS=http://127.0.0.1:38131' ".env.example" need_grep 'POOL_SUBMIT_RPC_URLS=' ".env.example" need_grep 'NODE_RPC_URLS: .*http://127.0.0.1:38131' "docker-compose.yml" -need_grep 'POOL_SUBMIT_RPC_URLS: .*POOL_SUBMIT_RPC_URLS' "docker-compose.yml" -need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: .*POOL_RPC_ROUTER_NODE_HEALTH_ENABLED' "docker-compose.yml" -need_grep 'POOL_SUBMIT_STALE_BLOCK_CANDIDATES: .*POOL_SUBMIT_STALE_BLOCK_CANDIDATES' "docker-compose.yml" + need_grep 'POOL_SUBMIT_RPC_URLS: .*POOL_SUBMIT_RPC_URLS' "docker-compose.yml" + need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_ENABLED: .*POOL_RPC_ROUTER_NODE_HEALTH_ENABLED' "docker-compose.yml" + need_grep 'POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS: .*POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS' "docker-compose.yml" + need_grep 'POOL_SUBMIT_STALE_BLOCK_CANDIDATES: .*POOL_SUBMIT_STALE_BLOCK_CANDIDATES' "docker-compose.yml" + need_grep 'POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS: .*POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS' "docker-compose.yml" need_grep 'POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED: .*POOL_SUBMIT_BLOCK_HEADER_V2_ENABLED' "docker-compose.yml" need_grep 'POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD: .*POOL_STALE_RACE_CLIENT_RESEND_THRESHOLD' "docker-compose.yml" need_grep 'POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD: .*POOL_EXPIRED_JOB_CLIENT_RECONNECT_THRESHOLD' "docker-compose.yml" @@ -396,9 +409,13 @@ need_grep 'install_stack_support_services' "ops/release-install.sh" need_grep 'ops/install-p2p-services.sh' "ops/release-install.sh" need_grep 'BDAG_NETWORK_TOPOLOGY=auto' ".env.example" need_grep 'BDAG_ASIC_LAN_CIDRS=' ".env.example" +need_grep '^POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp$' ".env.example" need_grep 'BDAG_DOCKER_BRIDGE_CIDRS=172.16.0.0/12' ".env.example" need_grep 'BDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS=0' ".env.example" need_grep 'BDAG_ASIC_LAN_CIDRS: .*BDAG_ASIC_LAN_CIDRS' "docker-compose.yml" +need_grep '\$\{BDAG_HOST_ARP_TABLE_PATH:-/proc/net/arp\}:/host/proc/net/arp:ro' "docker-compose.yml" +need_grep 'POOL_ASIC_ARP_TABLE_PATH: .*POOL_ASIC_ARP_TABLE_PATH' "docker-compose.yml" +need_grep 'POOL_ASIC_MAC_ALLOWLIST: .*POOL_ASIC_MAC_ALLOWLIST' "docker-compose.yml" need_grep 'BDAG_DOCKER_BRIDGE_CIDRS: .*BDAG_DOCKER_BRIDGE_CIDRS' "docker-compose.yml" need_grep 'BDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS: .*BDAG_ALLOW_DOCKER_BRIDGE_ASIC_IPS' "docker-compose.yml" need_grep 'BDAG_FASTSYNC_PEER_ORDERING=p2p-latency' ".env.example" diff --git a/scripts/validate-release-build.sh b/scripts/validate-release-build.sh index d4d3ede..5d4fdea 100755 --- a/scripts/validate-release-build.sh +++ b/scripts/validate-release-build.sh @@ -87,15 +87,27 @@ reject_grep 'SNAPSHOT_PATH=.*release-downloads/latest\.bdsnap' ".env.example" reject_grep 'SNAPSHOT_PATH:-\./latest\.bdsnap' "docker-compose.yml" need_grep '^DASHBOARD_HOST_PORT=8088$' ".env.example" need_grep '\$\{DASHBOARD_HOST_PORT:-8088\}:8088' "docker-compose.yml" -need_grep '^P2P_PORT=8150$' ".env.example" -need_grep 'P2P_PORT: \$\{P2P_PORT:-8150\}' "docker-compose.yml" +need_grep '^P2P_PORT=8155$' ".env.example" +need_grep 'P2P_PORT: \$\{P2P_PORT:-8155\}' "docker-compose.yml" need_grep '^port=8150$' "node.conf.example" reject_grep '^port=8154$' "node.conf.example" +need_grep '^BDAG_NODE_OBSOLETE_HEIGHT=20$' ".env.example" +need_grep '^BDAG_NODE_MINING_NO_PENDING_TX=0$' ".env.example" +need_grep 'BDAG_NODE_OBSOLETE_HEIGHT: \$\{BDAG_NODE_OBSOLETE_HEIGHT:-20\}' "docker-compose.yml" +need_grep 'BDAG_NODE_MINING_NO_PENDING_TX: \$\{BDAG_NODE_MINING_NO_PENDING_TX:-0\}' "docker-compose.yml" need_grep '^BDAG_NODE_RPC_URLS=node=http://127\.0\.0\.1:38131$' ".env.example" reject_grep '^BDAG_NODE_RPC_URLS=node=http://node:38131$' ".env.example" -need_grep '^DASHBOARD_SRC_CONTEXT=../redis-dash$' ".env.example" -need_grep '^DASHBOARD_REPO=https://github[.]com/BlockdagEngineering/redis-dash[.]git$' ".env.example" -need_grep 'dashboard_src: \$\{DASHBOARD_SRC_CONTEXT:-\.\./redis-dash\}' "docker-compose.yml" + need_grep '^DASHBOARD_SRC_CONTEXT=../redis-dash$' ".env.example" + need_grep '^DASHBOARD_REPO=https://github[.]com/BlockdagEngineering/redis-dash[.]git$' ".env.example" + need_grep '^NODE_DATA_DIR=./data/node$' ".env.example" + need_grep '^MINING_POOL_ADDRESS=$' ".env.example" + reject_grep '^MINING_POOL_ADDRESS=0x[0-9a-fA-F]{40}$' ".env.example" + need_grep '\$\{NODE_DATA_DIR:-\./data/node\}:/var/lib/bdagStack/node' "docker-compose.yml" + need_grep 'dashboard_src: \$\{DASHBOARD_SRC_CONTEXT:-\.\./redis-dash\}' "docker-compose.yml" + need_grep '^POOL_ASIC_ARP_TABLE_PATH=/host/proc/net/arp$' ".env.example" + need_grep '\$\{BDAG_HOST_ARP_TABLE_PATH:-/proc/net/arp\}:/host/proc/net/arp:ro' "docker-compose.yml" + need_grep 'POOL_ASIC_ARP_TABLE_PATH: .*POOL_ASIC_ARP_TABLE_PATH' "docker-compose.yml" + need_grep 'POOL_ASIC_MAC_ALLOWLIST: .*POOL_ASIC_MAC_ALLOWLIST' "docker-compose.yml" reject_grep 'COLLECTOR_SRC_CONTEXT' ".env.example" reject_grep 'collector_src:' "docker-compose.yml" reject_grep '^ collector:' "docker-compose.override.yml" @@ -118,18 +130,27 @@ reject_grep 'ARG COLLECTOR_REF' "dockerfile" reject_grep 'COPY --from=collector_src \. /opt/collector' "dockerfile" need_grep '^BOOTSTRAP_PEER_ADDRESSES=/ip4/13\.57\.132\.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus$' ".env.example" need_grep 'BOOTSTRAP_PEER_ADDRESSES: \$\{BOOTSTRAP_PEER_ADDRESSES:-\}' "docker-compose.yml" -need_grep '^addpeer=/ip4/13\.57\.132\.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus$' "node.conf.example" -reject_grep '^addpeer=/ip4/52\.8\.80\.249/tcp/8150/p2p/' "node.conf.example" -reject_grep '^addpeer=/ip4/192\.168\.' "node.conf.example" + need_grep '^addpeer=/ip4/13\.57\.132\.47/tcp/8150/p2p/16Uiu2HAmDynYpWjWmgVGf9qVWvDdLnJ3ybVgDmFexizR4zMereus$' "node.conf.example" + reject_grep '^addpeer=/ip4/52\.8\.80\.249/tcp/8150/p2p/' "node.conf.example" + reject_grep '^addpeer=/ip4/192\.168\.' "node.conf.example" + need_grep '^POOL_RPC_ROUTER_NODE_HEALTH_MIN_CONSENSUS_PEERS=2$' ".env.example" + need_grep '^POOL_RECENT_STALE_BLOCK_CANDIDATE_SUBMIT_GRACE_MS=1750$' ".env.example" + need_grep '^POOL_PREEMPTIVE_BLOCK_CANDIDATE_REFRESH_INTERVAL_MS=20$' ".env.example" need_grep 'stack-redis--linux-amd64\.zip' "README.md" need_grep 'stack-redis--linux-arm64\.zip' "README.md" need_grep 'First-Install Mining Safety Gates' "README.md" need_grep 'native `getTemplateHealth`' "README.md" +need_grep 'Node config edits and service recreates are cold/idle operations' "README.md" need_grep 'node=http://127\.0\.0\.1:38131' "README.md" need_grep '2026-06-20 Hardening Addendum' "docs/redis-dash-deploy-difficulties-2026-06-19.md" need_grep 'P2P Port Drift Was the Root Failure' "docs/redis-dash-deploy-difficulties-2026-06-19.md" need_grep 'Dashboard Sync Status Must Follow Native Template Health' "docs/redis-dash-deploy-difficulties-2026-06-19.md" +need_grep '2026-06-21 Live Node Mutation Incident' "docs/redis-dash-deploy-difficulties-2026-06-19.md" need_grep 'Do Not Source Complex `[.]env` Files In Shell Checks' "docs/redis-dash-deploy-difficulties-2026-06-19.md" +need_grep 'Live node mutation guard' "docs/release-readiness-gates.html" +need_grep 'template coinbase .* expected' "scripts/release-readiness-check.py" +need_grep 'missing non-zero expected mining address' "scripts/release-readiness-check.py" +need_grep 'getBlockTemplate.*expected payout address' "docs/release-readiness-gates.html" need_grep 'release-payload.env' "scripts/release/installers/install-unix-common.sh" need_grep 'release-payload.env' "scripts/release/installers/install-windows.ps1" diff --git a/scripts/validate-stack-defaults.py b/scripts/validate-stack-defaults.py index 4d6e077..5a822b6 100644 --- a/scripts/validate-stack-defaults.py +++ b/scripts/validate-stack-defaults.py @@ -90,8 +90,14 @@ def assert_shell_defaults_match( ) -> None: path = root / rel_path text = path.read_text(encoding="utf-8") + current_function = "" for lineno, raw_line in enumerate(text.splitlines(), start=1): stripped = raw_line.strip() + function_match = re.match(r"^([A-Za-z_][A-Za-z0-9_]*)\(\)\s*\{$", stripped) + if function_match: + current_function = function_match.group(1) + elif stripped == "}": + current_function = "" if not stripped or stripped.startswith("#"): continue if "set_env_value" not in stripped and "ensure_env_value" not in stripped and "ensure_stack_default_env_value" not in stripped: @@ -121,6 +127,8 @@ def assert_shell_defaults_match( continue if value.startswith("$") or "$(" in value or "${" in value: continue + if rel_path == "ops/release-install.sh" and current_function == "configure_node_mining_env": + continue expected = defaults[key] if value != expected: errors.append(f"{rel_path}:{lineno}: {key} default {value!r}, expected {expected!r}") diff --git a/sql/pool-schema.sql b/sql/pool-schema.sql index fa41859..f047e20 100644 --- a/sql/pool-schema.sql +++ b/sql/pool-schema.sql @@ -38,6 +38,19 @@ CREATE TABLE IF NOT EXISTS block_submissions ( accepted BOOLEAN NOT NULL DEFAULT FALSE, outcome TEXT NOT NULL, message TEXT, + worker TEXT, + job_id TEXT, + client_addr TEXT, + remote_host TEXT, + asic_mac TEXT, + lane_id TEXT NOT NULL DEFAULT 'unknown', + lane_identity_source TEXT NOT NULL DEFAULT 'unknown', + protocol_variant TEXT NOT NULL DEFAULT 'stratum-v1-basic', + extranonce_subscribed BOOLEAN NOT NULL DEFAULT FALSE, + backend_attempted BOOLEAN NOT NULL DEFAULT TRUE, + pdiff DOUBLE PRECISION, + share_diff DOUBLE PRECISION, + job_age_ms BIGINT, created_at TIMESTAMP DEFAULT NOW() ); @@ -47,6 +60,16 @@ ON block_submissions (created_at); CREATE INDEX IF NOT EXISTS block_submissions_outcome_created_idx ON block_submissions (outcome, created_at); +CREATE INDEX IF NOT EXISTS block_submissions_lane_created_idx +ON block_submissions (lane_id, created_at); + +CREATE INDEX IF NOT EXISTS block_submissions_lane_outcome_created_idx +ON block_submissions (lane_id, outcome, created_at); + +CREATE INDEX IF NOT EXISTS block_submissions_asic_created_idx +ON block_submissions (asic_mac, created_at) +WHERE asic_mac IS NOT NULL; + CREATE TABLE IF NOT EXISTS payouts ( id SERIAL PRIMARY KEY, tx_hash TEXT UNIQUE NOT NULL,