diff --git a/scripts/export-testnet-state.sh b/scripts/export-testnet-state.sh index 81812a9..a6b21d4 100755 --- a/scripts/export-testnet-state.sh +++ b/scripts/export-testnet-state.sh @@ -23,6 +23,6 @@ testnet_state_file="$OUTPUT_DIR/testnet-state.json" docker run -it \ -v ${TESTNET_DEPLOYMENT_DIR}/data/laconicd-data:/root/testnet-deployment/.laconicd \ cerc/laconicd:local bash -c "laconicd export --home /root/testnet-deployment/.laconicd" \ - | jq .app_state.onboarding > "$testnet_state_file" + | jq > "$testnet_state_file" echo "Exported state from testnet to $testnet_state_file" diff --git a/scripts/generate-mainnet-genesis.sh b/scripts/generate-mainnet-genesis.sh index ae249fe..c76e75e 100755 --- a/scripts/generate-mainnet-genesis.sh +++ b/scripts/generate-mainnet-genesis.sh @@ -23,6 +23,19 @@ cp $TESTNET_STATE_FILE $MAINNET_GENESIS_DIR/testnet-state.json # -------- +docker run -it \ + -v ./$MAINNET_GENESIS_DIR:/root/.laconicd \ + -v ./scripts:/scripts \ + -e "CHAIN_ID=$CHAIN_ID" \ + cerc/laconicd:local bash -c "/scripts/init-mainnet.sh" + +# -------- + +# Carry over state from testnet to mainnet +python transfer-state.py + +# -------- + # Run a script with cerc/laconicd:local to generate the genesis file # with onboarding module state and given allocations docker run -it \ @@ -30,7 +43,6 @@ docker run -it \ -v ./scripts:/scripts \ -e "CHAIN_ID=$CHAIN_ID" \ -e "EARLY_SUPPORTS_ACC_ADDRESS=$EARLY_SUPPORTS_ACC_ADDRESS" \ - -e "LPS_LOCKUP_ACC_ADDRESS=$LPS_LOCKUP_ACC_ADDRESS" \ cerc/laconicd:local bash -c "/scripts/genesis.sh" # Copy over the genesis file to output folder diff --git a/scripts/genesis.sh b/scripts/genesis.sh index 385b715..31589b1 100755 --- a/scripts/genesis.sh +++ b/scripts/genesis.sh @@ -8,13 +8,13 @@ set -u CHAINID=${CHAINID:-"laconic-mainnet"} MONIKER=${MONIKER:-"mainnet-node"} +KEYRING="test" NODE_HOME="/root/.laconicd" EARLY_SUPPORTS_ACC_ADDRESS=${EARLY_SUPPORTS_ACC_ADDRESS} -LPS_LOCKUP_ACC_ADDRESS=${LPS_LOCKUP_ACC_ADDRESS} -if [ -z "$EARLY_SUPPORTS_ACC_ADDRESS" ] || [ -z "$LPS_LOCKUP_ACC_ADDRESS" ]; then - echo "EARLY_SUPPORTS_ACC_ADDRESS or LPS_LOCKUP_ACC_ADDRESS not provided, exiting..." +if [ -z "$EARLY_SUPPORTS_ACC_ADDRESS" ]; then + echo "EARLY_SUPPORTS_ACC_ADDRESS not provided, exiting..." exit 1 fi @@ -23,25 +23,19 @@ fi EARLY_SUPPORTS_ALLOC="12960000000000000000000" # Early supports: 12960 * 10^18 alps (10% of total supply) LOCKUP_ALLOC="116640000000000000000000" # Lockup: 116640 * 10^18 alps (90% of total supply) LPS_LOCKUP_MODULE_ACCOUNT="lps_lockup" +LPS_DENOM="alps" testnet_state_file="$NODE_HOME/testnet-state.json" mainnet_genesis_file="$NODE_HOME/config/genesis.json" -laconicd config set client chain-id $CHAINID -laconicd init $MONIKER --chain-id $CHAINID --default-denom alnt - -# Import required state -jq --slurpfile nested $testnet_state_file '.app_state.auth = $nested[0].app_state' "$mainnet_genesis_file" > tmp.$$.json && mv tmp.$$.json "$mainnet_genesis_file" -jq --slurpfile nested $testnet_state_file '.consensus.auth = $nested[0].consensus' "$mainnet_genesis_file" > tmp.$$.json && mv tmp.$$.json "$mainnet_genesis_file" - # Update any module params if required here # Perform alps allocations -laconicd genesis add-genesis-account $ADDRESS $EARLY_SUPPORTS$DENOM --keyring-backend $KEYRING +laconicd genesis add-genesis-account $EARLY_SUPPORTS_ACC_ADDRESS $EARLY_SUPPORTS_ALLOC$LPS_DENOM --keyring-backend $KEYRING # Use zero address to add an account for lps_lockup zero_address="laconic1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqklcls0" -laconicd genesis add-genesis-account $zero_address $EARLY_SUPPORTS$DENOM --keyring-backend $KEYRING --module-name $LPS_LOCKUP_MODULE_ACCOUNT +laconicd genesis add-genesis-account $zero_address $LOCKUP_ALLOC$LPS_DENOM --keyring-backend $KEYRING --module-name $LPS_LOCKUP_MODULE_ACCOUNT # Update the lps_lockup address in bank module state lps_lockup_address=$(jq -r '.app_state.auth.accounts[] | select(.name == "lps_lockup") | .base_account.address' $HOME/.laconicd/config/genesis.json) diff --git a/scripts/init-mainnet.sh b/scripts/init-mainnet.sh new file mode 100755 index 0000000..bcb887a --- /dev/null +++ b/scripts/init-mainnet.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Exit on error +set -e +set -u + +# Note: Needs to be run in a docker container with image cerc/laconicd:local + +CHAINID=${CHAINID:-"laconic-mainnet"} +MONIKER=${MONIKER:-"mainnet-node"} + +laconicd config set client chain-id $CHAINID +laconicd init $MONIKER --chain-id $CHAINID --default-denom alnt diff --git a/scripts/transfer-state.py b/scripts/transfer-state.py new file mode 100644 index 0000000..5d67b54 --- /dev/null +++ b/scripts/transfer-state.py @@ -0,0 +1,112 @@ +import json + +from decimal import Decimal +from collections import defaultdict + +mainnet_genesis_dir="mainnet-genesis" +testnet_state_file=f"{mainnet_genesis_dir}/testnet-state.json" +mainnet_genesis_file=f"{mainnet_genesis_dir}/config/genesis.json" + +with open(testnet_state_file) as f: + testnet_state = json.load(f) + +with open(mainnet_genesis_file) as f: + mainnet_state = json.load(f) + +#------ + +# Import required module state +mainnet_state["app_state"]["auth"] = testnet_state["app_state"]["auth"] +mainnet_state["app_state"]["bond"] = testnet_state["app_state"]["bond"] +mainnet_state["app_state"]["bank"] = testnet_state["app_state"]["bank"] +mainnet_state["app_state"]["registry"] = testnet_state["app_state"]["registry"] +mainnet_state["app_state"]["slashing"]["params"] = testnet_state["app_state"]["slashing"]["params"] +mainnet_state["consensus"]["params"] = testnet_state["consensus"]["params"] + +#------ + +# Allocate back delegation stakes + +# Build map of address -> extra balance to add (as integers) +deltas = {} +for delegation in testnet_state["app_state"]["staking"]["delegations"]: + addr = delegation["delegator_address"] + amount = int(Decimal(delegation["shares"])) + deltas[addr] = deltas.get(addr, 0) + amount + +# Now apply the deltas to existing balances +supply_increment = 0 +for balance in mainnet_state["app_state"]["bank"]["balances"]: + addr = balance["address"] + if addr in deltas: + for coin in balance["coins"]: + if coin["denom"] == "alnt": + coin["amount"] = str(int(coin["amount"]) + deltas[addr]) + supply_increment += deltas[addr] + break + del deltas[addr] + +# Increase the total supply +for coin in mainnet_state["app_state"]["bank"]["supply"]: + if coin["denom"] == "alnt": + coin["amount"] = str(int(coin["amount"]) + supply_increment) + +#------ + +# Remove non-required module accounts + +# Addresses to remove +addresses_to_remove = { + "bonded_tokens_pool", + "not_bonded_tokens_pool", + "distribution" +} + +# Remove from auth.accounts and get their addresses +removed_addresses = set() +new_accounts = [] +for account in mainnet_state["app_state"]["auth"]["accounts"]: + account_type = account.get("@type", "") + if "ModuleAccount" in account_type and account.get("name") in addresses_to_remove: + removed_addresses.add(account["base_account"]["address"]) + continue + new_accounts.append(account) + +mainnet_state["app_state"]["auth"]["accounts"] = new_accounts + +# Remove from bank.balances and tally removed amounts +new_balances = [] +removed_amounts = defaultdict(int) + +for bal in mainnet_state["app_state"]["bank"]["balances"]: + if bal["address"] in removed_addresses: + # Skip this account + for coin in bal["coins"]: + denom = coin["denom"] + amount = int(coin["amount"]) + removed_amounts[denom] += amount + + continue + + new_balances.append(bal) + +mainnet_state["app_state"]["bank"]["balances"] = new_balances + +# Reduce from bank supply +new_supply = [] +for coin in mainnet_state["app_state"]["bank"]["supply"]: + denom = coin["denom"] + amount = int(coin["amount"]) + amount -= removed_amounts.get(denom, 0) + new_supply.append({ + "denom": denom, + "amount": str(amount) + }) + +mainnet_state["app_state"]["bank"]["supply"] = new_supply + +#------ + +# Write back modified state +with open(mainnet_genesis_file, "w") as f: + json.dump(mainnet_state, f, indent=2)