Skip to content

Commit

Permalink
feat: add chain-initiator tool to use mainnet snapshot in localnet
Browse files Browse the repository at this point in the history
  • Loading branch information
snobbee committed Dec 7, 2023
1 parent cbfba94 commit ba28cf7
Show file tree
Hide file tree
Showing 16 changed files with 795 additions and 0 deletions.
38 changes: 38 additions & 0 deletions scripts/chain-initiator/account-unmarshal-json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"encoding/json"
"fmt"
)

func (a *Account) UnmarshalJSON(data []byte) error {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}

// Set the Type field from the raw data
typeStr, ok := raw["@type"].(string)
if !ok {
return fmt.Errorf("type field is missing or invalid")
}
a.Type = typeStr

switch a.Type {
case "/cosmos.auth.v1beta1.BaseAccount":
var ba BaseAccount
if err := json.Unmarshal(data, &ba); err != nil {
return err
}
a.BaseAccount = &ba
case "/cosmos.auth.v1beta1.ModuleAccount":
var ma ModuleAccount
if err := json.Unmarshal(data, &ma); err != nil {
return err
}
a.ModuleAccount = &ma
default:
return fmt.Errorf("unknown account type: %s", a.Type)
}
return nil
}
19 changes: 19 additions & 0 deletions scripts/chain-initiator/add-genesis-account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"log"
"os/exec"
)

func addGenesisAccount(cmdPath, address, balance, homePath string) {
// Command and arguments
args := []string{"add-genesis-account", address, balance + "rowan", "--home", homePath}

// Execute the command
if err := exec.Command(cmdPath, args...).Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// If execution reaches here, the command was successful
log.Printf("add genesis account with address %s, balance: %s and home path %s successfully", address, balance, homePath)
}
29 changes: 29 additions & 0 deletions scripts/chain-initiator/add-key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import (
"encoding/json"
"log"
"os/exec"
)

func addKey(cmdPath, name, homePath, keyringBackend string) string {
// Command and arguments
args := []string{"keys", "add", name, "--home", homePath, "--keyring-backend", keyringBackend, "--output", "json"}

// Execute the command
output, err := exec.Command(cmdPath, args...).CombinedOutput()
if err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// Unmarshal the JSON output
var keyOutput KeyOutput
if err := json.Unmarshal(output, &keyOutput); err != nil {
log.Fatalf("Failed to unmarshal JSON output: %v", err)
}

// Log the address
log.Printf("add key with name %s, home path: %s, keyring backend %s and address %s successfully", name, homePath, keyringBackend, keyOutput.Address)

return keyOutput.Address
}
19 changes: 19 additions & 0 deletions scripts/chain-initiator/collect-gentxs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"log"
"os/exec"
)

func collectGentxs(cmdPath, homePath string) {
// Command and arguments
args := []string{"collect-gentxs", "--home", homePath}

// Execute the command
if err := exec.Command(cmdPath, args...).Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// If execution reaches here, the command was successful
log.Printf("collect gen txs with home path %s successfully", homePath)
}
17 changes: 17 additions & 0 deletions scripts/chain-initiator/filter-accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

func filterAccounts(accounts []Account, filterAddresses []string) []Account {
filterMap := make(map[string]struct{})
for _, addr := range filterAddresses {
filterMap[addr] = struct{}{}
}

var newAccounts []Account

Check failure on line 9 in scripts/chain-initiator/filter-accounts.go

View workflow job for this annotation

GitHub Actions / lint

Consider pre-allocating `newAccounts` (prealloc)
for _, account := range accounts {
if shouldFilterAccount(account, filterMap) {
continue
}
newAccounts = append(newAccounts, account)
}
return newAccounts
}
24 changes: 24 additions & 0 deletions scripts/chain-initiator/filter-balances.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

func filterBalances(balances []banktypes.Balance, filterAddresses []string) ([]banktypes.Balance, sdk.Coins) {
filterMap := make(map[string]struct{})
for _, addr := range filterAddresses {
filterMap[addr] = struct{}{}
}

var newBalances []banktypes.Balance

Check failure on line 14 in scripts/chain-initiator/filter-balances.go

View workflow job for this annotation

GitHub Actions / lint

Consider pre-allocating `newBalances` (prealloc)
var coinsToRemove sdk.Coins
for _, balance := range balances {
if _, exists := filterMap[balance.Address]; exists {
coinsToRemove = coinsToRemove.Add(balance.Coins...)
continue
}
newBalances = append(newBalances, balance)
}
return newBalances, coinsToRemove
}
19 changes: 19 additions & 0 deletions scripts/chain-initiator/gen-tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"log"
"os/exec"
)

func genTx(cmdPath, name, balance, chainId, homePath, keyringBackend string) {
// Command and arguments
args := []string{"gentx", name, balance + "rowan", "--chain-id", chainId, "--home", homePath, "--keyring-backend", keyringBackend}

// Execute the command
if err := exec.Command(cmdPath, args...).Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// If execution reaches here, the command was successful
log.Printf("gen tx with name %s, balance: %s, chain id %s, home path %s and keyring backend %s successfully", name, balance, chainId, homePath, keyringBackend)
}
19 changes: 19 additions & 0 deletions scripts/chain-initiator/init-chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"log"
"os/exec"
)

func initChain(cmdPath, moniker, chainId, homePath string) {
// Command and arguments
args := []string{"init", moniker, "--chain-id", chainId, "--home", homePath}

// Execute the command
if err := exec.Command(cmdPath, args...).Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// If execution reaches here, the command was successful
log.Printf("init chain with moniker %s, chain id %s and home path: %s successfully", moniker, chainId, homePath)
}
121 changes: 121 additions & 0 deletions scripts/chain-initiator/initiator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package main

import (
"log"

app "github.com/Sifchain/sifnode/app"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/spf13/cobra"
)

const (
moniker = "node"
chainId = "sifchain-1"
keyringBackend = "test"
validatorKeyName = "validator"
validatorBalance = "4000000000000000000000000000"
)

func main() {
var rootCmd = &cobra.Command{
Use: "initiator [cmd_path] [home_path] [genesis_file_path]",
Short: "Chain Initiator is a tool for modifying genesis files",
Long: `A tool for performing various operations on genesis files of a blockchain setup.`,
Args: cobra.ExactArgs(3), // Expect exactly two arguments
Run: func(cmd *cobra.Command, args []string) {
cmdPath := args[0] // sifnoded
homePath := args[1] // /tmp/node
genesisFilePath := args[2] // /tmp/genesis.json

// set address prefix
app.SetConfig(false)

// remove home path
removeHome(homePath)

// init chain
initChain(cmdPath, moniker, chainId, homePath)

// add validator key
validatorAddress := addKey(cmdPath, validatorKeyName, homePath, keyringBackend)

// add genesis account
addGenesisAccount(cmdPath, validatorAddress, validatorBalance, homePath)

// generate genesis tx
genTx(cmdPath, validatorKeyName, validatorBalance, chainId, homePath, keyringBackend)

// collect genesis txs
collectGentxs(cmdPath, homePath)

// validate genesis
validateGenesis(cmdPath, homePath)

genesis, err := readGenesisFile(genesisFilePath)
if err != nil {
log.Fatalf("Error reading genesis file: %v", err)
}

genesisInitFilePath := homePath + "/config/genesis.json"
genesisInit, err := readGenesisFile(genesisInitFilePath)
if err != nil {
log.Fatalf("Error reading initial genesis file: %v", err)
}

filterAccountAddresses := []string{
"sif1harggtyrlukcfrtmpgjzptsnaedcdh38qqknp2", // multisig account with missing pubkeys
}
filterBalanceAddresses := []string{
"sif1harggtyrlukcfrtmpgjzptsnaedcdh38qqknp2",
authtypes.NewModuleAddress("distribution").String(),
authtypes.NewModuleAddress("bonded_tokens_pool").String(),
authtypes.NewModuleAddress("not_bonded_tokens_pool").String(),
}

var coinsToRemove sdk.Coins

genesis.AppState.Auth.Accounts = filterAccounts(genesis.AppState.Auth.Accounts, filterAccountAddresses)
genesis.AppState.Bank.Balances, coinsToRemove = filterBalances(genesis.AppState.Bank.Balances, filterBalanceAddresses)

newValidatorBalance, ok := sdk.NewIntFromString("4000000000000000000000000000")
if !ok {
panic("invalid number")
}
newValidatorBalanceCoin := sdk.NewCoin("rowan", newValidatorBalance)

// update supply
genesis.AppState.Bank.Supply = genesis.AppState.Bank.Supply.Sub(coinsToRemove).Add(newValidatorBalanceCoin)

// Add new validator account and balance
genesis.AppState.Auth.Accounts = append(genesis.AppState.Auth.Accounts, genesisInit.AppState.Auth.Accounts[0])
genesis.AppState.Bank.Balances = append(genesis.AppState.Bank.Balances, genesisInit.AppState.Bank.Balances[0])

// reset staking data
stakingParams := genesis.AppState.Staking.Params
genesis.AppState.Staking = genesisInit.AppState.Staking
genesis.AppState.Staking.Params = stakingParams

// reset slashing data
genesis.AppState.Slashing = genesisInit.AppState.Slashing

// reset distribution data
genesis.AppState.Distribution = genesisInit.AppState.Distribution

// set genutil from genesisInit
genesis.AppState.Genutil = genesisInit.AppState.Genutil

outputFilePath := homePath + "/config/genesis.json"
if err := writeGenesisFile(outputFilePath, genesis); err != nil {
log.Fatalf("Error writing genesis file: %v", err)
}

// start chain
start(cmdPath, homePath)
},
}

if err := rootCmd.Execute(); err != nil {
log.Fatalf("Error executing command: %v", err)
}
}
23 changes: 23 additions & 0 deletions scripts/chain-initiator/read-genesis-file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"bufio"
"encoding/json"
"fmt"
"os"
)

func readGenesisFile(filePath string) (Genesis, error) {
var genesis Genesis
file, err := os.Open(filePath)
if err != nil {
return genesis, fmt.Errorf("error opening file: %w", err)
}
defer file.Close()

if err := json.NewDecoder(bufio.NewReader(file)).Decode(&genesis); err != nil {
return genesis, fmt.Errorf("error decoding JSON: %w", err)
}

return genesis, nil
}
19 changes: 19 additions & 0 deletions scripts/chain-initiator/remove-home.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"log"
"os/exec"
)

func removeHome(homePath string) {
// Command and arguments
args := []string{"-rf", homePath}

// Execute the command
if err := exec.Command("rm", args...).Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}

// If execution reaches here, the command was successful
log.Printf("removed home path %s successfully", homePath)
}
15 changes: 15 additions & 0 deletions scripts/chain-initiator/should-filter-account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

func shouldFilterAccount(account Account, filterAddresses map[string]struct{}) bool {
if account.BaseAccount != nil {
if _, exists := filterAddresses[account.BaseAccount.Address]; exists {
return true
}
}
if account.ModuleAccount != nil {
if _, exists := filterAddresses[account.ModuleAccount.BaseAccount.Address]; exists {
return true
}
}
return false
}
24 changes: 24 additions & 0 deletions scripts/chain-initiator/start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"log"
"os"
"os/exec"
)

func start(cmdPath, homePath string) {
// Command and arguments
args := []string{"start", "--home", homePath}

// Set up the command
cmd := exec.Command(cmdPath, args...)

// Attach command's stdout and stderr to os.Stdout and os.Stderr
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Execute the command and stream the output
if err := cmd.Run(); err != nil {
log.Fatalf("Command execution failed: %v", err)
}
}
Loading

0 comments on commit ba28cf7

Please sign in to comment.