#!/usr/bin/env bash
# shellcheck disable=SC2178
set +e

PWD=$(cd "$(dirname "$0")" && pwd)
export PATH="$PWD:$PATH"

NODE3="${1}_chain33_1"
CLI="docker exec ${NODE3} /root/chain33-cli"

NODE2="${1}_chain32_1"
CLI2="docker exec ${NODE2} /root/chain33-cli"

NODE1="${1}_chain31_1"
CLI3="docker exec ${NODE1} /root/chain33-cli"

NODE4="${1}_chain30_1"
CLI4="docker exec ${NODE4} /root/chain33-cli"

NODE5="${1}_chain29_1"
CLI5="docker exec ${NODE5} /root/chain33-cli"

NODE6="${1}_chain28_1"
CLI6="docker exec ${NODE6} /root/chain33-cli"

containers=("${NODE1}" "${NODE2}" "${NODE3}" "${NODE4}" "${NODE5}" "${NODE6}")
forkContainers=("${CLI3}" "${CLI2}" "${CLI}" "${CLI4}" "${CLI5}" "${CLI6}")

export COMPOSE_PROJECT_NAME="$1"

sedfix=""
if [ "$(uname)" == "Darwin" ]; then
    sedfix=".bak"
fi

DAPP=""
if [ -n "${2}" ]; then
    DAPP=$2
fi

DAPP_TEST_FILE=""

if [ -n "${DAPP}" ]; then
    testfile="fork-test.sh"
    if [ -e "$testfile" ]; then
        # shellcheck source=/dev/null
        source "${testfile}"
        DAPP_TEST_FILE="$testfile"
    fi

    DAPP_COMPOSE_FILE="docker-compose-${DAPP}.yml"
    if [ -e "$DAPP_COMPOSE_FILE" ]; then
        export COMPOSE_FILE="docker-compose.yml:${DAPP_COMPOSE_FILE}"

    fi

fi

system_coins_file="system/coins/fork-test.sh"
# shellcheck source=/dev/null
source "${system_coins_file}"

echo "=========== # env setting ============="
echo "DAPP=$DAPP"
echo "DAPP_TEST_FILE=$DAPP_TEST_FILE"
echo "COMPOSE_FILE=$COMPOSE_FILE"
echo "COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME"
echo "CLI=$CLI"

function base_init() {
    # update test environment
    sed -i $sedfix 's/^Title.*/Title="local"/g' chain33.toml
    sed -i $sedfix 's/^TestNet=.*/TestNet=true/g' chain33.toml

    # p2p
    sed -i $sedfix 's/^seeds=.*/seeds=["chain33:13802","chain32:13802","chain31:13802","chain30:13802","chain29:13802","chain28:13802"]/g' chain33.toml
    sed -i $sedfix 's/^enable=.*/enable=true/g' chain33.toml
    sed -i $sedfix 's/^isSeed=.*/isSeed=true/g' chain33.toml
    sed -i $sedfix 's/^innerSeedEnable=.*/innerSeedEnable=false/g' chain33.toml
    sed -i $sedfix 's/^useGithub=.*/useGithub=false/g' chain33.toml

    # rpc
    sed -i $sedfix 's/^jrpcBindAddr=.*/jrpcBindAddr="0.0.0.0:8801"/g' chain33.toml
    sed -i $sedfix 's/^grpcBindAddr=.*/grpcBindAddr="0.0.0.0:8802"/g' chain33.toml
    sed -i $sedfix 's/^whitelist=.*/whitelist=["localhost","127.0.0.1","0.0.0.0"]/g' chain33.toml

    # wallet
    sed -i $sedfix 's/^minerdisable=.*/minerdisable=false/g' chain33.toml

}

function start() {
    # docker-compose ps
    docker-compose ps

    # remove exsit container
    docker-compose down

    # create and run docker-compose container
    #    docker-compose -f docker-compose.yml -f docker-compose-para.yml up --build -d
    docker-compose up --build -d

    local SLEEP=30
    echo "=========== sleep ${SLEEP}s ============="
    sleep ${SLEEP}

    docker-compose ps

    # query node run status

    ${CLI} block last_header
    ${CLI} net info

    ${CLI} net peer
    peersCount=$(${CLI} net peer | jq '.[] | length')
    echo "${peersCount}"
    if [ "${peersCount}" -lt 2 ]; then
        sleep 20
        peersCount=$(${CLI} net peer | jq '.[] | length')
        echo "${peersCount}"
        if [ "${peersCount}" -lt 2 ]; then
            echo "peers error"
            exit 1
        fi
    fi

    #echo "=========== # create seed for wallet ============="
    #seed=$(${CLI} seed generate -l 0 | jq ".seed")
    #if [ -z "${seed}" ]; then
    #    echo "create seed error"
    #    exit 1
    #fi

    echo "=========== # save seed to wallet ============="
    result=$(${CLI} seed save -p 1314fuzamei -s "tortoise main civil member grace happy century convince father cage beach hip maid merry rib" | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "save seed to wallet error seed, result: ${result}"
        exit 1
    fi

    sleep 1

    echo "=========== # unlock wallet ============="
    result=$(${CLI} wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        exit 1
    fi

    sleep 1

    echo "=========== # import private key returnAddr ============="
    result=$(${CLI} account import_key -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944 -l returnAddr | jq ".label")
    echo "${result}"
    if [ -z "${result}" ]; then
        exit 1
    fi

    sleep 1

    echo "=========== # import private key mining ============="
    result=$(${CLI} account import_key -k 4257D8692EF7FE13C68B65D6A52F03933DB2FA5CE8FAF210B5B8B80C721CED01 -l minerAddr | jq ".label")
    echo "${result}"
    if [ -z "${result}" ]; then
        exit 1
    fi

    sleep 1
    echo "=========== # close auto mining ============="
    result=$(${CLI} wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        exit 1
    fi

    ## 2nd mining
    echo "=========== # save seed to wallet ============="
    result=$(${CLI4} seed save -p 1314fuzamei -s "tortoise main civil member grace happy century convince father cage beach hip maid merry rib" | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "save seed to wallet error seed, result: ${result}"
        exit 1
    fi

    sleep 1

    echo "=========== # unlock wallet ============="
    result=$(${CLI4} wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        exit 1
    fi

    sleep 1

    echo "=========== # import private key returnAddr ============="
    result=$(${CLI4} account import_key -k 2AFF1981291355322C7A6308D46A9C9BA311AA21D94F36B43FC6A6021A1334CF -l returnAddr | jq ".label")
    echo "${result}"
    if [ -z "${result}" ]; then
        exit 1
    fi

    sleep 1

    echo "=========== # import private key mining ============="
    result=$(${CLI4} account import_key -k 2116459C0EC8ED01AA0EEAE35CAC5C96F94473F7816F114873291217303F6989 -l minerAddr | jq ".label")
    echo "${result}"
    if [ -z "${result}" ]; then
        exit 1
    fi

    sleep 1
    echo "=========== # close auto mining ============="
    result=$(${CLI4} wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        exit 1
    fi

    block_wait "${CLI}" 1

    echo "=========== check genesis hash ========== "
    ${CLI} block hash -t 0
    res=$(${CLI} block hash -t 0 | jq ".hash")
    count=$(echo "$res" | grep -c "0x67c58d6ba9175313f0468ae4e0ddec946549af7748037c2fdd5d54298afd20b6")
    if [ "${count}" != 1 ]; then
        echo "genesis hash error!"
        exit 1
    fi

    echo "=========== query height ========== "
    ${CLI} block last_header
    result=$(${CLI} block last_header | jq ".height")
    if [ "${result}" -lt 1 ]; then
        exit 1
    fi

    ${CLI} wallet status
    ${CLI} account list
    ${CLI} mempool list
}

function dapp_run() {
    if [ -e "$DAPP_TEST_FILE" ]; then
        ${DAPP} "${CLI}" "${1}"
    fi

}

function optDockerfun() {
    #############################################
    #1 第一种分叉构造：首先两条链进行共同挖矿，然后再分
    # 别进行挖矿，即两条链上发生分叉时候的交易是不同的。
    #############################################
    forkType1
    #############################################
    #2 第二种分叉构造：包括第一组docker,第二组docker，
    # 以及公共节点docker，首先共同挖矿，然后停掉第二组
    # docker，备份公共节点docker数据库，在公共节点docker
    # 上创建交易，签名交易，记录签名，发送，然后关掉第一组
    # docker，然后将公共节点docker数据库恢复到备份状态，
    # 然后启动第二组docker,然后发送刚刚记录签名的交易。
    # 最后启动全部节点共同挖矿
    #############################################
    forkType2
}

function forkType1() {
    echo "=========== 开始进行类型1分叉测试 ========== "
    base_init
    dapp_run forkInit

    start
    optDockerPart1
    #############################################
    #此处根据具体需求加入；如从钱包中转入某个具体合约账户
    #1 初始化交易余额
    dapp_run forkConfig

    #############################################
    optDockerPart2
    #############################################
    #此处根据具体需求加入在一条测试链中发送测试数据
    #2 构造第一条链中交易
    dapp_run forkAGroupRun

    #############################################
    optDockerPart3
    #############################################
    #此处根据具体需求加入在第二条测试链中发送测试数据
    #3 构造第二条链中交易
    dapp_run forkBGroupRun

    #############################################
    optDockerPart4
    loopCount=30 #循环次数，每次循环休眠时间100s
    checkBlockHashfun $loopCount

    #############################################
    #此处根据具体需求加入结果检查
    #4 检查交易结果
    dapp_run forkCheckRst

    #############################################
    echo "=========== 类型1分叉测试结束 ========== "
}

function forkType2() {
    echo "=========== 开始进行类型2分叉测试 ========== "
    base_init
    dapp_run fork2Init

    start

    optDockerPart1
    #############################################
    #此处根据具体需求加入；如从钱包中转入某个具体合约账户
    #1 初始化交易余额
    initCoinsAccount
    dapp_run fork2Config

    #############################################
    type2_optDockerPart2
    #############################################
    #此处根据具体需求加入在一条测试链中发送测试数据
    #2 构造第一条链中交易
    genFirstChainCoinstx
    dapp_run fork2AGroupRun
    #############################################
    type2_optDockerPart3
    #############################################
    #此处根据具体需求加入在第二条测试链中发送测试数据
    #3 构造第二条链中交易
    genSecondChainCoinstx
    dapp_run fork2BGroupRun

    #############################################
    type2_optDockerPart4
    loopCount=30 #循环次数，每次循环休眠时间100s
    checkBlockHashfun $loopCount
    #############################################
    #此处根据具体需求加入结果检查
    #4 检查交易结果
    checkCoinsResult
    dapp_run fork2CheckRst
    #############################################
    echo "=========== 类型2分叉测试结束 ========== "
}

function optDockerPart1() {
    echo "====== 区块生成中 ======"
    #sleep 100
    block_wait_timeout "${CLI}" 10 100

    loopCount=20
    for ((i = 0; i < loopCount; i++)); do
        name="${CLI}"
        time=2
        needCount=6
        peersCount "${name}" $time $needCount
        peerStatus=$?
        if [ $peerStatus -eq 1 ]; then
            name="${CLI4}"
            peersCount "${name}" $time $needCount
            peerStatus=$?
            if [ $peerStatus -eq 0 ]; then
                break
            fi
        else
            break
        fi
        #检查是否超过了最大检测次数
        if [ $i -ge $((loopCount - 1)) ]; then
            echo "====== peers not enough ======"
            exit 1
        fi
    done

    return 1

}

function optDockerPart2() {
    checkMineHeight
    status=$?
    if [ $status -eq 0 ]; then
        echo "====== All peers is the same height ======"
    else
        echo "====== All peers is the different height, syn blockchain fail======"
        exit 1
    fi

    echo "==================================="
    echo "====== 第一步：第一组docker挖矿======"
    echo "==================================="

    echo "======停止第二组docker ======"
    docker pause "${NODE4}" "${NODE5}" "${NODE6}"

    echo "======开启第一组docker节点挖矿======"
    sleep 3
    result=$($CLI wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet2 mine fail"
        exit 1
    fi

    name=$CLI
    time=60
    needCount=3

    peersCount "${name}" $time $needCount
    peerStatus=$?
    if [ $peerStatus -eq 1 ]; then
        echo "====== peers not enough ======"
        exit 1
    fi

}

function optDockerPart3() {
    echo "======第一组docker节点挖矿中======"
    block_wait_timeout "${CLI}" 5 100
    echo "======停止第一组docker节点挖矿======"
    result=$($CLI wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "stop wallet2 mine fail"
        exit 1
    fi

    echo "====== 第一组内部同步中 ======"
    names[0]="${NODE3}"
    names[1]="${NODE2}"
    names[2]="${NODE1}"
    syn_block_timeout "${CLI}" 3 50 "${names[@]}"

    echo "======================================="
    echo "======== 第二步：第二组docker挖矿 ======="
    echo "======================================="

    echo "======停止第一组docker======"
    docker pause "${NODE1}" "${NODE2}" "${NODE3}"

    echo "======sleep 5s======"
    sleep 5

    echo "======启动第二组docker======"
    docker unpause "${NODE4}" "${NODE5}" "${NODE6}"

    echo "======sleep 20s======"
    sleep 5
    result=$($CLI4 wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "wallet1 unlock fail"
        exit 1
    fi

    name="${CLI4}"
    time=60
    needCount=3

    peersCount "${name}" $time $needCount
    peerStatus=$?
    if [ $peerStatus -eq 1 ]; then
        echo "====== peers not enough ======"
        exit 1
    fi

    echo "======开启第二组docker节点挖矿======"
    sleep 1
    result=$($CLI4 wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet1 mine fail"
        exit 1
    fi

    names[0]="${NODE4}"
    names[1]="${NODE5}"
    names[2]="${NODE6}"
    syn_block_timeout "${CLI4}" 2 100 "${names[@]}"

}

function optDockerPart4() {
    echo "======第二组docker节点挖矿中======"
    block_wait_timeout "${CLI4}" 3 50
    echo "====== 第二组内部同步中 ======"
    names[0]="${NODE4}"
    names[1]="${NODE5}"
    names[2]="${NODE6}"
    syn_block_timeout "${CLI4}" 3 50 "${names[@]}"

    echo "======================================="
    echo "====== 第三步：两组docker共同挖矿 ======="
    echo "======================================="

    echo "======启动第一组docker======"
    docker unpause "${NODE1}" "${NODE2}" "${NODE3}"

    echo "======sleep 20s======"
    sleep 5
    result=$($CLI wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "wallet2 unlock fail"
        exit 1
    fi
    echo "======开启第一组docker节点挖矿======"
    sleep 1
    result=$($CLI wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet2 mine fail"
        exit 1
    fi

    echo "======两组docker节点共同挖矿中======"
    block_wait_timeout "${CLI}" 5 100
}

function copyData() {
    name="${NODE3}"
    sleep 1
    docker exec "${name}" mkdir beifen
    sleep 1
    docker exec "${name}" cp -r datadir beifen
    sleep 1
}

function restoreData() {
    name="${NODE3}"
    sleep 1
    docker exec "${name}" rm -rf datadir
    sleep 1
    docker exec "${name}" cp -r beifen/datadir ./
    sleep 1
}

function type2_optDockerPart2() {
    checkMineHeight
    status=$?
    if [ $status -eq 0 ]; then
        echo "====== All peers is the same height ======"
    else
        echo "====== All peers is the different height, syn blockchain fail======"
        exit 1
    fi

    echo "=============== 备份公共节点数据 =============="
    copyData

    echo "==================================="
    echo "====== 第一步：第一组docker挖矿======"
    echo "==================================="

    echo "======停止第二组docker ======"
    docker pause "${NODE4}" "${NODE5}" "${NODE6}"

    echo "======开启第一组docker节点挖矿======"
    sleep 3
    result=$($CLI wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet2 mine fail"
        exit 1
    fi

    name=$CLI
    time=60
    needCount=3

    peersCount "${name}" $time $needCount
    peerStatus=$?
    if [ $peerStatus -eq 1 ]; then
        echo "====== peers not enough ======"
        exit 1
    fi

}

function type2_optDockerPart3() {
    echo "======第一组docker节点挖矿中======"
    block_wait_timeout "${CLI}" 5 100
    echo "======停止第一组docker节点挖矿======"
    result=$($CLI wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "stop wallet2 mine fail"
        exit 1
    fi

    echo "====== 第一组内部同步中 ======"
    names[0]="${NODE3}"
    names[1]="${NODE2}"
    names[2]="${NODE1}"
    syn_block_timeout "${CLI}" 3 50 "${names[@]}"

    echo "======================================="
    echo "======== 第二步：第二组docker挖矿 ======="
    echo "======================================="

    echo "======停止第一组中除公共节点的docker======"
    docker pause "${NODE1}" "${NODE2}"

    echo "=============== 恢复公共节点数据 =============="
    restoreData
    docker pause "${NODE3}"

    echo "======sleep 5s======"
    sleep 5

    echo "======启动第二组docker======"
    docker unpause "${NODE3}" "${NODE4}" "${NODE5}" "${NODE6}"

    name="${CLI}"
    time=60
    needCount=4

    peersCount "${name}" $time $needCount
    peerStatus=$?
    if [ $peerStatus -eq 1 ]; then
        echo "====== peers not enough ======"
        exit 1
    fi

    echo "======sleep 20s======"
    sleep 20
    echo "======开启第二组docker节点挖矿======"

    result=$($CLI wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "wallet1 unlock fail"
        exit 1
    fi

    sleep 1
    result=$($CLI wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet1 mine fail"
        exit 1
    fi

    sleep 1
    result=$($CLI4 wallet unlock -p 1314fuzamei -t 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "wallet2 unlock fail"
        exit 1
    fi

    sleep 1
    result=$($CLI4 wallet auto_mine -f 1 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "start wallet2 mine fail"
        exit 1
    fi

    names[0]="${NODE3}"
    names[1]="${NODE4}"
    names[2]="${NODE5}"
    names[3]="${NODE6}"
    syn_block_timeout "${CLI}" 2 100 "${names[@]}"

}

function type2_optDockerPart4() {
    echo "======第二组docker节点挖矿中======"
    block_wait_timeout "${CLI}" 3 50
    echo "====== 第二组内部同步中 ======"
    names[0]="${NODE4}"
    names[1]="${NODE5}"
    names[2]="${NODE6}"
    names[3]="${NODE3}"
    syn_block_timeout "${CLI}" 3 50 "${names[@]}"

    echo "======================================="
    echo "====== 第三步：两组docker共同挖矿 ======="
    echo "======================================="

    echo "======启动第一组docker======"
    docker unpause "${NODE1}" "${NODE2}"

    echo "======两组docker节点共同挖矿中======"
    block_wait_timeout "${CLI}" 5 100
}

function checkMineHeight() {
    result=$($CLI4 wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "stop wallet1 mine fail"
        return 1
    fi
    sleep 1
    result=$($CLI wallet auto_mine -f 0 | jq ".isok")
    if [ "${result}" = "false" ]; then
        echo "stop wallet2 mine fail"
        return 1
    fi

    echo "====== stop all wallet mine success ======"

    echo "====== syn blockchain ======"
    syn_block_timeout "${CLI}" 5 50 "${containers[@]}"

    height=0
    height1=$($CLI4 block last_header | jq ".height")
    sleep 1
    height2=$($CLI block last_header | jq ".height")
    if [ "${height2}" -ge "${height1}" ]; then
        height=$height2
        printf '当前最大高度 %s \n' "${height}"
    else
        height=$height1
        printf '当前最大高度 %s \n' "${height}"
    fi

    if [ "${height}" -eq 0 ]; then
        echo "获取当前最大高度失败"
        return 1
    fi
    loopCount=20
    for ((k = 0; k < ${#forkContainers[*]}; k++)); do
        for ((j = 0; j < loopCount; j++)); do
            height1=$(${forkContainers[$k]} block last_header | jq ".height")
            if [ "${height1}" -gt "${height}" ]; then #如果大于说明区块还没有完全产生完，替换期望高度
                height=$height1
                printf '查询 %s 目前区块最高高度为第 %s \n' "${containers[$k]}" "${height}"
            elif [ "${height1}" -eq "${height}" ]; then #找到目标高度
                break
            else
                printf '查询 %s 第 %d 次，当前高度 %d, 需要高度%d, 同步中，sleep 60s 后查询\n' "${containers[$k]}" $j "${height1}" "${height}"
                sleep 60
            fi
            #检查是否超过了最大检测次数
            if [ $j -ge $((loopCount - 1)) ]; then
                echo "====== syn blockchain fail======"
                return 1
            fi
        done
    done

    return 0
}

function peersCount() {
    name=$1
    time=$2
    needCount=$3

    for ((i = 0; i < time; i++)); do
        peersCount=$($name net peer | jq '.[] | length')
        printf '查询节点 %s ,所需节点数 %d ,当前节点数 %s \n' "${name}" "${needCount}" "${peersCount}"
        if [ "${peersCount}" = "$needCount" ]; then
            echo "============= 符合节点数要求 ============="
            return 0
        else
            echo "============= 休眠 30s 继续查询 ============="
            sleep 30
        fi
    done

    return 1
}

function checkBlockHashfun() {
    echo "====== syn blockchain ======"
    syn_block_timeout "${CLI}" 10 50 "${containers[@]}"

    height=0
    hash=""
    height1=$($CLI block last_header | jq ".height")
    sleep 1
    height2=$($CLI4 block last_header | jq ".height")
    if [ "${height2}" -ge "${height1}" ]; then
        height=$height2
        printf "主链为 $CLI 当前最大高度 %d \\n" "${height}"
        sleep 1
        hash=$($CLI block hash -t "${height}" | jq ".hash")
    else
        height=$height1
        printf "主链为 $CLI4 当前最大高度 %d \\n" "${height}"
        sleep 1
        hash=$($CLI4 block hash -t "${height}" | jq ".hash")
    fi

    for ((j = 0; j < $1; j++)); do
        for ((k = 0; k < ${#forkContainers[*]}; k++)); do
            sleep 1
            height0[$k]=$(${forkContainers[$k]} block last_header | jq ".height")
            if [ "${height0[$k]}" -ge "${height}" ]; then
                sleep 1
                hash0[$k]=$(${forkContainers[$k]} block hash -t "${height}" | jq ".hash")
            else
                hash0[$k]="${forkContainers[$k]}"
            fi
        done

        if [ "${hash0[0]}" = "${hash}" ] && [ "${hash0[1]}" = "${hash}" ] && [ "${hash0[2]}" = "${hash}" ] && [ "${hash0[3]}" = "${hash}" ] && [ "${hash0[4]}" = "${hash}" ] && [ "${hash0[5]}" = "${hash}" ]; then
            echo "syn blockchain success break"
            break
        else
            if [ "${hash0[1]}" = "${hash0[0]}" ] && [ "${hash0[2]}" = "${hash0[0]}" ] && [ "${hash0[3]}" = "${hash0[0]}" ] && [ "${hash0[4]}" = "${hash0[0]}" ] && [ "${hash0[5]}" = "${hash0[0]}" ]; then
                echo "syn blockchain success break"
                break
            fi
        fi
        peersCount=0
        peersCount=$(${forkContainers[0]} net peer | jq '.[] | length')
        printf '第 %d 次，未查询到网络同步，当前节点数 %d 个，100s后查询\n' $j "${peersCount}"
        sleep 100
        #检查是否超过了最大检测次数
        var=$(($1 - 1))
        if [ $j -ge "${var}" ]; then
            echo "====== syn blockchain fail======"
            exit 1
        fi
    done
    echo "====== syn blockchain success======"
}

# $1 name
# $2 txHash
function txQuery() {
    name=$1
    txHash=$2
    result=$($name tx query -s "${txHash}" | jq -r ".receipt.tyname")
    if [ "${result}" = "ExecOk" ]; then
        return 0
    fi
    return 1
}

function block_wait_timeout() {
    if [ "$#" -lt 3 ]; then
        echo "wrong block_wait params"
        exit 1
    fi
    cur_height=$(${1} block last_header | jq ".height")
    expect=$((cur_height + ${2}))
    count=0
    while true; do
        new_height=$(${1} block last_header | jq ".height")
        if [ "${new_height}" -ge "${expect}" ]; then
            break
        fi
        count=$((count + 1))
        sleep 1
        if [ $count -ge "${3}" ]; then
            echo "====== block wait timeout ======"
            break
        fi
    done
    echo "wait new block $count s"
}

function syn_block_timeout() {
    #${1} name
    #${2} minHeight
    #${3} timeout
    #${4} names

    names=${4}

    if [ "$#" -lt 3 ]; then
        echo "wrong block_wait params"
        exit 1
    fi
    cur_height=$(${1} block last_header | jq ".height")
    expect=$((cur_height + ${2}))
    count=0
    while true; do
        new_height=$(${1} block last_header | jq ".height")
        if [ "${new_height}" -lt "${expect}" ]; then
            count=$((count + 1))
            sleep 1
        else
            isSyn="true"
            for ((k = 0; k < ${#names[@]}; k++)); do
                sync_status=$(docker exec "${names[$k]}" /root/chain33-cli net is_sync)
                if [ "${sync_status}" = "false" ]; then
                    isSyn="false"
                    break
                fi
                count=$((count + 1))
                sleep 1
            done
            if [ "${isSyn}" = "true" ]; then
                break
            fi
        fi

        if [ $count -ge $(($3 + 1)) ]; then
            echo "====== syn block wait timeout ======"
            break
        fi

    done
    echo "wait block $count s"
}

function block_wait() {
    if [ "$#" -lt 2 ]; then
        echo "wrong block_wait params"
        exit 1
    fi
    cur_height=$(${1} block last_header | jq ".height")
    expect=$((cur_height + ${2}))
    count=0
    while true; do
        new_height=$(${1} block last_header | jq ".height")
        if [ "${new_height}" -ge "${expect}" ]; then
            break
        fi
        count=$((count + 1))
        sleep 1
    done
    echo "wait new block $count s"
}

optDockerfun
