#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WIRE_SYSIO_DIR="${WIRE_SYSIO_DIR:-${ROOT_DIR}/wire-sysio}" BUILD_DIR="${WIRE_SYSIO_BUILD_DIR:-${WIRE_SYSIO_DIR}/build/debug-docker}" VCPKG_DIR="${BUILD_DIR}/vcpkg_installed/x64-linux" WIRE_CDT_DIR="${WIRE_CDT_DIR:-${ROOT_DIR}/wire-cdt}" WIRE_CDT_BUILD_DIR="${WIRE_CDT_BUILD_DIR:-${WIRE_CDT_DIR}/build/debug-docker}" TARGET_ROOT="${WIRE_NODE_ROOT:-/srv/wire-node/chain-001}" CONTRACTS_DIR="${WIRE_SYSIO_CONTRACTS_DIR:-${BUILD_DIR}/contracts}" WIRE_ARTIFACTS_DIR="${WIRE_ARTIFACTS_DIR:-${ROOT_DIR}/.local/e2e}" WIRE_SYSIO_SUBMODULE_FALLBACK_DIR="${WIRE_SYSIO_SUBMODULE_FALLBACK_DIR:-${ROOT_DIR}/wire-sysio}" WIRE_SYSIO_BUILD_FALLBACK_DIR="${WIRE_SYSIO_BUILD_FALLBACK_DIR:-${ROOT_DIR}/wire-sysio/build/debug-docker}" WIRE_ETH_DEPOT_ACCOUNT="${WIRE_ETH_DEPOT_ACCOUNT:-sysio.dpeth}" WIRE_SOL_DEPOT_ACCOUNT="${WIRE_SOL_DEPOT_ACCOUNT:-sysio.dpsol}" WIRE_INSTASWAP_ACCOUNT="${WIRE_INSTASWAP_ACCOUNT:-sysio.iswap}" WIRE_WYIELD_ACCOUNT="${WIRE_WYIELD_ACCOUNT:-sysio.wyield}" WIRE_TEST_USER_ACCOUNT="${WIRE_TEST_USER_ACCOUNT:-userinsta1}" WIRE_BATCH_OPERATOR_ETH_ACCOUNT="${WIRE_BATCH_OPERATOR_ETH_ACCOUNT:-bopeth111111}" WIRE_BATCH_OPERATOR_SOL_ACCOUNT="${WIRE_BATCH_OPERATOR_SOL_ACCOUNT:-bopsol111111}" WIRE_BATCH_OPERATOR_ETH_WIRE_PROVIDER_ID="${WIRE_BATCH_OPERATOR_ETH_WIRE_PROVIDER_ID:-wire-bopeth-1}" WIRE_BATCH_OPERATOR_SOL_WIRE_PROVIDER_ID="${WIRE_BATCH_OPERATOR_SOL_WIRE_PROVIDER_ID:-wire-bopsol-1}" WIRE_DEPOT_ACCOUNT_RAM_BYTES="${WIRE_DEPOT_ACCOUNT_RAM_BYTES:-8388608}" WIRE_APP_ACCOUNT_RAM_BYTES="${WIRE_APP_ACCOUNT_RAM_BYTES:-8388608}" WIRE_OPERATOR_ACCOUNT_RAM_BYTES="${WIRE_OPERATOR_ACCOUNT_RAM_BYTES:-1048576}" WIRE_USER_ACCOUNT_RAM_BYTES="${WIRE_USER_ACCOUNT_RAM_BYTES:-1048576}" CMAKE_COMMON_ARGS=( -S "${WIRE_SYSIO_DIR}" -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE="${WIRE_SYSIO_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" -DCMAKE_C_COMPILER=/opt/clang/clang-18/bin/clang -DCMAKE_CXX_COMPILER=/opt/clang/clang-18/bin/clang++ -DCMAKE_INSTALL_PREFIX=/opt/wire -DCMAKE_PREFIX_PATH="/opt/wire;/opt/clang/clang-18;${WIRE_CDT_BUILD_DIR}" -DENABLE_TESTS=OFF -DBUILD_SYSTEM_CONTRACTS=ON -DCDT_ROOT="${WIRE_CDT_BUILD_DIR}" ) cleanup() { if [[ -n "${NODEOP_PID:-}" ]]; then kill "${NODEOP_PID}" >/dev/null 2>&1 || true wait "${NODEOP_PID}" >/dev/null 2>&1 || true fi if [[ -n "${KIOD_PID:-}" ]]; then kill "${KIOD_PID}" >/dev/null 2>&1 || true wait "${KIOD_PID}" >/dev/null 2>&1 || true fi } dir_is_empty() { local path="$1" [[ -d "${path}" ]] && [[ -z "$(find "${path}" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]] } link_submodule_from_fallback() { local relative_path="$1" local target_path="${WIRE_SYSIO_DIR}/${relative_path}" local source_path="${WIRE_SYSIO_SUBMODULE_FALLBACK_DIR}/${relative_path}" local link_source if [[ ! -e "${source_path}" ]]; then return fi link_source="$(realpath --relative-to "$(dirname "${target_path}")" "${source_path}")" if [[ -L "${target_path}" ]] && [[ "$(readlink "${target_path}")" == "${link_source}" ]]; then return fi if [[ -L "${target_path}" ]]; then ln -sfn "${link_source}" "${target_path}" return fi if dir_is_empty "${target_path}"; then rmdir "${target_path}" fi if [[ ! -e "${target_path}" ]]; then ln -sfn "${link_source}" "${target_path}" fi } hydrate_wire_submodules() { link_submodule_from_fallback "libraries/appbase" link_submodule_from_fallback "vcpkg" } hydrate_wire_build_cache() { local target_install_dir="${BUILD_DIR}/vcpkg_installed" local source_install_dir="${WIRE_SYSIO_BUILD_FALLBACK_DIR}/vcpkg_installed" local link_source if [[ ! -d "${source_install_dir}" ]]; then return fi mkdir -p "$(dirname "${target_install_dir}")" link_source="$(realpath --relative-to "$(dirname "${target_install_dir}")" "${source_install_dir}")" if [[ -L "${target_install_dir}" ]] && [[ "$(readlink "${target_install_dir}")" == "${link_source}" ]]; then return fi if [[ -L "${target_install_dir}" ]]; then ln -sfn "${link_source}" "${target_install_dir}" return fi if [[ -d "${target_install_dir}" ]] && [[ ! -f "${BUILD_DIR}/bin/nodeop" ]]; then mv "${target_install_dir}" "${target_install_dir}.partial.$(date +%s)" fi if [[ ! -e "${target_install_dir}" ]]; then ln -s "${link_source}" "${target_install_dir}" fi } require_wire_submodules() { hydrate_wire_submodules if [[ ! -f "${WIRE_SYSIO_DIR}/vcpkg/bootstrap-vcpkg.sh" ]] || [[ ! -f "${WIRE_SYSIO_DIR}/libraries/appbase/CMakeLists.txt" ]]; then echo "wire-sysio submodules are missing. Run: git -C ${WIRE_SYSIO_DIR} submodule update --init --recursive" >&2 exit 1 fi } write_pkgconfig_shims() { local pc_dir="$1" local lib_dir="$2" mkdir -p "${pc_dir}" cat >"${pc_dir}/libssl.pc" <"${pc_dir}/libcrypto.pc" </dev/null 2>&1 || true write_pkgconfig_shims "${VCPKG_DIR}/lib/pkgconfig" "${VCPKG_DIR}/lib" write_pkgconfig_shims "${VCPKG_DIR}/debug/lib/pkgconfig" "${VCPKG_DIR}/debug/lib" cmake \ "${CMAKE_COMMON_ARGS[@]}" \ -DOPENSSL_INCLUDE_DIR="${VCPKG_DIR}/include" \ -DOPENSSL_SSL_LIBRARY="${VCPKG_DIR}/debug/lib/libssl.a" \ -DOPENSSL_CRYPTO_LIBRARY="${VCPKG_DIR}/debug/lib/libbscrypto.a" } build_wire_sysio() { cmake --build \ "${BUILD_DIR}" \ --target contracts_project-build sys-util clio kiod nodeop \ -- -j"$(nproc)" } configure_chain() { export WIRE_ROOT="${WIRE_ROOT:-${WIRE_SYSIO_DIR}}" export PATH="${BUILD_DIR}/bin:${PATH}" sys-util chain-configure \ --contracts="${CONTRACTS_DIR}" \ --target="${TARGET_ROOT}" \ --template=aio \ --overwrite } extract_secret() { local pattern="$1" local file="$2" sed -n "s/^${pattern}: //p" "${file}" } run_clio() { clio -u http://127.0.0.1:8887 "$@" } run_clio_wallet() { clio wallet "$@" } key_file_for_account() { local account_name="$1" echo "${TARGET_ROOT}/secrets/${account_name}_key.txt" } ensure_local_key() { local account_name="$1" local key_file local output key_file="$(key_file_for_account "${account_name}")" if [[ -f "${key_file}" ]]; then return fi output="$(clio create key --to-console)" printf '%s\n' "${output}" >"${key_file}" } import_key_if_needed() { local key_file="$1" local private_key private_key="$(extract_secret "Private key" "${key_file}")" if [[ -z "${private_key}" ]]; then echo "Missing private key in ${key_file}" >&2 exit 1 fi run_clio_wallet import --name default --private-key "${private_key}" >/dev/null 2>&1 || true } ensure_account_exists() { local account_name="$1" local key_file local public_key key_file="$(key_file_for_account "${account_name}")" public_key="$(extract_secret "Public key" "${key_file}")" if [[ -z "${public_key}" ]]; then echo "Missing public key in ${key_file}" >&2 exit 1 fi if run_clio get account "${account_name}" >/dev/null 2>&1; then return fi run_clio create account sysio "${account_name}" "${public_key}" >/dev/null } ensure_contract_deployed() { local account_name="$1" local contract_dir="$2" run_clio set contract "${account_name}" "${contract_dir}" >/dev/null } ensure_account_ram() { local account_name="$1" local ram_bytes="$2" run_clio push action \ sysio \ setacctram \ "[\"${account_name}\",${ram_bytes}]" \ -p sysio@active >/dev/null } ensure_account_code_permission() { local account_name="$1" run_clio set account permission \ "${account_name}" \ active \ --add-code \ -p "${account_name}@active" >/dev/null } table_has_rows() { local account="$1" local scope="$2" local table="$3" ! run_clio get table "${account}" "${scope}" "${table}" | grep -Eq '"rows": *\[[[:space:]]*\]' } currency_exists() { local code="$1" local output output="$(run_clio get currency stats sysio.token "${code}" 2>/dev/null || true)" grep -q "\"${code}\"" <<<"${output}" } wire_env_path() { echo "${WIRE_ARTIFACTS_DIR}/wire.env" } write_wire_artifacts() { local eth_key_file local sol_key_file local eth_pub local eth_priv local sol_pub local sol_priv local out_file mkdir -p "${WIRE_ARTIFACTS_DIR}" eth_key_file="$(key_file_for_account "${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}")" sol_key_file="$(key_file_for_account "${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}")" eth_pub="$(extract_secret "Public key" "${eth_key_file}")" eth_priv="$(extract_secret "Private key" "${eth_key_file}")" sol_pub="$(extract_secret "Public key" "${sol_key_file}")" sol_priv="$(extract_secret "Private key" "${sol_key_file}")" out_file="$(wire_env_path)" cat >"${out_file}" </dev/null fi if ! table_has_rows "${WIRE_SOL_DEPOT_ACCOUNT}" "${WIRE_SOL_DEPOT_ACCOUNT}" depotstate; then run_clio push action \ "${WIRE_SOL_DEPOT_ACCOUNT}" \ init \ "[3,\"sysio.token\"]" \ -p "${WIRE_SOL_DEPOT_ACCOUNT}@active" >/dev/null fi if ! currency_exists WIRE; then run_clio push action \ sysio.token \ create \ "[\"${WIRE_INSTASWAP_ACCOUNT}\",\"1000000000.0000 WIRE\"]" \ -p sysio.token@active >/dev/null fi if ! currency_exists LIQETH; then run_clio push action \ sysio.token \ create \ "[\"${WIRE_INSTASWAP_ACCOUNT}\",\"1000000000.0000 LIQETH\"]" \ -p sysio.token@active >/dev/null fi if ! currency_exists LIQSOL; then run_clio push action \ sysio.token \ create \ "[\"${WIRE_INSTASWAP_ACCOUNT}\",\"1000000000.0000 LIQSOL\"]" \ -p sysio.token@active >/dev/null fi if ! run_clio get currency balance sysio.token "${WIRE_INSTASWAP_ACCOUNT}" WIRE | grep -q "WIRE"; then run_clio push action \ sysio.token \ issue \ "[\"${WIRE_INSTASWAP_ACCOUNT}\",\"1000000.0000 WIRE\",\"local pool seed\"]" \ -p "${WIRE_INSTASWAP_ACCOUNT}@active" >/dev/null fi if ! table_has_rows "${WIRE_INSTASWAP_ACCOUNT}" "${WIRE_INSTASWAP_ACCOUNT}" config; then run_clio push action \ "${WIRE_INSTASWAP_ACCOUNT}" \ init \ "[\"sysio.token\",\"${WIRE_ETH_DEPOT_ACCOUNT}\",\"${WIRE_SOL_DEPOT_ACCOUNT}\",\"${WIRE_INSTASWAP_ACCOUNT}\",\"4,WIRE\",\"4,LIQETH\",\"4,LIQSOL\",30]" \ -p "${WIRE_INSTASWAP_ACCOUNT}@active" >/dev/null fi if ! table_has_rows "${WIRE_WYIELD_ACCOUNT}" "${WIRE_WYIELD_ACCOUNT}" config; then run_clio push action \ "${WIRE_WYIELD_ACCOUNT}" \ init \ "[\"sysio.token\",\"${WIRE_INSTASWAP_ACCOUNT}\",\"${WIRE_ETH_DEPOT_ACCOUNT}\",\"${WIRE_SOL_DEPOT_ACCOUNT}\",\"4,WIRE\",\"4,LIQETH\",\"4,LIQSOL\"]" \ -p "${WIRE_WYIELD_ACCOUNT}@active" >/dev/null fi run_clio push action \ "${WIRE_INSTASWAP_ACCOUNT}" \ ondeposit \ "[2,\"${eth_seed_id}\",\"${WIRE_INSTASWAP_ACCOUNT}\",\"10000.0000 LIQETH\"]" \ -p "${WIRE_ETH_DEPOT_ACCOUNT}@active" >/dev/null run_clio push action \ "${WIRE_INSTASWAP_ACCOUNT}" \ ondeposit \ "[3,\"${sol_seed_id}\",\"${WIRE_INSTASWAP_ACCOUNT}\",\"10000.0000 LIQSOL\"]" \ -p "${WIRE_SOL_DEPOT_ACCOUNT}@active" >/dev/null if ! run_clio get table "${WIRE_ETH_DEPOT_ACCOUNT}" 2 knownops | grep -q "\"wire_account\": \"${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}\""; then run_clio push action \ "${WIRE_ETH_DEPOT_ACCOUNT}" \ regoperator \ "[\"${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}\",1,\"010101010101010101010101010101010101010101010101010101010101010101\",\"0202020202020202020202020202020202020202020202020202020202020202\",\"1000.0000 WIRE\"]" \ -p "${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}@active" >/dev/null fi if ! run_clio get table "${WIRE_SOL_DEPOT_ACCOUNT}" 3 knownops | grep -q "\"wire_account\": \"${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}\""; then run_clio push action \ "${WIRE_SOL_DEPOT_ACCOUNT}" \ regoperator \ "[\"${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}\",1,\"030303030303030303030303030303030303030303030303030303030303030303\",\"0404040404040404040404040404040404040404040404040404040404040404\",\"1000.0000 WIRE\"]" \ -p "${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}@active" >/dev/null fi if ! run_clio get table "${WIRE_ETH_DEPOT_ACCOUNT}" 2 knownops | grep -q "\"wire_account\": \"${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}\",.*\"status\": 1"; then run_clio push action \ "${WIRE_ETH_DEPOT_ACCOUNT}" \ activateop \ "[\"${WIRE_BATCH_OPERATOR_ETH_ACCOUNT}\"]" \ -p "${WIRE_ETH_DEPOT_ACCOUNT}@active" >/dev/null fi if ! run_clio get table "${WIRE_SOL_DEPOT_ACCOUNT}" 3 knownops | grep -q "\"wire_account\": \"${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}\",.*\"status\": 1"; then run_clio push action \ "${WIRE_SOL_DEPOT_ACCOUNT}" \ activateop \ "[\"${WIRE_BATCH_OPERATOR_SOL_ACCOUNT}\"]" \ -p "${WIRE_SOL_DEPOT_ACCOUNT}@active" >/dev/null fi write_wire_artifacts } start_nodeop() { local key_file="${TARGET_ROOT}/secrets/sysio_key.txt" local wallet_pw local sys_public_key local sys_private_key local sys_bls_public_key local sys_bls_private_key local ready=0 wallet_pw="$(cat "${TARGET_ROOT}/secrets/sysio_wallet_pw.txt")" sys_public_key="$(extract_secret "Public key" "${key_file}")" sys_private_key="$(extract_secret "Private key" "${key_file}")" sys_bls_public_key="$(extract_secret "BLS Pub key" "${key_file}")" sys_bls_private_key="$(extract_secret "BLS Priv key" "${key_file}")" pkill -9 kiod >/dev/null 2>&1 || true kiod --wallet-dir "${TARGET_ROOT}/wallet" >"${TARGET_ROOT}/kiod.log" 2>&1 & KIOD_PID=$! sleep 1 clio wallet unlock --name default --password "${wallet_pw}" >/dev/null nodeop \ --config-dir "${TARGET_ROOT}/config" \ --data-dir "${TARGET_ROOT}/data" \ --genesis-json "${TARGET_ROOT}/config/genesis.json" \ --contracts-console \ --signature-provider "wire-${sys_public_key},wire,wire,${sys_public_key},KEY:${sys_private_key}" \ --signature-provider "wire-bls-${sys_bls_public_key},wire,wire_bls,${sys_bls_public_key},KEY:${sys_bls_private_key}" \ >"${TARGET_ROOT}/nodeop.log" 2>&1 & NODEOP_PID=$! for _ in $(seq 1 30); do if clio -u http://127.0.0.1:8887 get info >/dev/null 2>&1; then ready=1 break fi sleep 1 done if [[ "${ready}" -ne 1 ]]; then echo "nodeop did not become ready within 30 seconds" >&2 tail -n 80 "${TARGET_ROOT}/nodeop.log" >&2 || true return 1 fi bootstrap_wire_stack wait "${NODEOP_PID}" } trap cleanup EXIT require_wire_submodules configure_wire_sysio build_wire_sysio configure_chain start_nodeop