#!/usr/bin/env bash start_height=$1 count=$2 if [ -z "${start_height}" ]; then echo echo "Error: missing start height." echo echo "Usage:" echo "block-timings.sh [target] [deviation] [power]" echo echo "startheight: a block height, preferably within the untrimmed range, to avoid data gaps" echo "count: the number of blocks to request and analyse after the start height. Default: 100" echo "target: the target block time in milliseconds. Originates from blockchain.json. Default: 60000" echo "deviation: the allowed block time deviation in milliseconds. Originates from blockchain.json. Default: 30000" echo "power: used when transforming key distance to a time offset. Originates from blockchain.json. Default: 0.2" echo exit fi target=$3 deviation=$4 power=$5 count=${count:=100} target=${target:=60000} deviation=${deviation:=30000} power=${power:=0.2} finish_height=$((start_height + count - 1)) height=$start_height echo "Settings:" echo "Target time offset: ${target}" echo "Deviation: ${deviation}" echo "Power transform: ${power}" echo function calculate_time_offset { local key_distance_ratio=$1 local transformed=$( echo "" | awk "END {print ${key_distance_ratio} ^ ${power}}") local time_offset=$(echo "${deviation}*2*${transformed}" | bc) time_offset=${time_offset%.*} echo $time_offset } function fetch_and_process_blocks { echo "Fetching blocks from height ${start_height} to ${finish_height}..." echo total_time_offset=0 errors=0 while [ "${height}" -le "${finish_height}" ]; do block_minting_info=$(curl -s "http://localhost:12391/blocks/byheight/${height}/mintinginfo") error=$(echo "${block_minting_info}" | jq -r .error) if [ "${error}" != "null" ]; then echo "Error fetching minting info for block ${height}" echo errors=$((errors+1)) height=$((height+1)) continue; fi # Parse minting info minter_level=$(echo "${block_minting_info}" | jq -r .minterLevel) key_distance_ratio=$(echo "${block_minting_info}" | jq -r .keyDistanceRatio) time_delta=$(echo "${block_minting_info}" | jq -r .timeDelta) time_offset=$(calculate_time_offset "${key_distance_ratio}") block_time=$((target-deviation+time_offset)) echo "=== BLOCK ${height} ===" echo "Minter level: ${minter_level}" echo "Key distance ratio: ${key_distance_ratio}" echo "Time offset: ${time_offset}" echo "Block time (real): ${time_delta}" echo "Block time (calculated): ${block_time}" if [ "${time_delta}" -ne "${block_time}" ]; then echo "WARNING: Block time mismatch. This is to be expected when using custom settings." fi echo total_time_offset=$((total_time_offset+block_time)) height=$((height+1)) done adjusted_count=$((count-errors)) if [ "${adjusted_count}" -eq 0 ]; then echo "No blocks were retrieved." echo exit; fi mean_time_offset=$((total_time_offset/adjusted_count)) time_offset_diff=$((mean_time_offset-target)) echo "===================" echo "===== SUMMARY =====" echo "===================" echo "Total blocks retrieved: ${adjusted_count}" echo "Total blocks failed: ${errors}" echo "Mean time offset: ${mean_time_offset}ms" echo "Target time offset: ${target}ms" echo "Difference from target: ${time_offset_diff}ms" echo } function estimate_key_distance_ratio_for_level { local level=$1 local example_key_distance="0.5" echo "(${example_key_distance}/${level})" } function estimate_block_timestamps { min_block_time=9999999 max_block_time=0 echo "===== BLOCK TIME ESTIMATES =====" for level in {1..10}; do example_key_distance_ratio=$(estimate_key_distance_ratio_for_level "${level}") time_offset=$(calculate_time_offset "${example_key_distance_ratio}") block_time=$((target-deviation+time_offset)) if [ "${block_time}" -gt "${max_block_time}" ]; then max_block_time=${block_time} fi if [ "${block_time}" -lt "${min_block_time}" ]; then min_block_time=${block_time} fi echo "Level: ${level}, time offset: ${time_offset}, block time: ${block_time}" done block_time_range=$((max_block_time-min_block_time)) echo "Range: ${block_time_range}" echo } fetch_and_process_blocks estimate_block_timestamps