From fa37d46ce5b5466b6824bc72b31197ed8d052306 Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Wed, 17 Apr 2024 14:40:03 +0100 Subject: [PATCH 1/3] feat: initial reconciliation transfer --- cmd/fetchd/cmd/genasiupgrade.go | 157 +++++++++++++++++++++++++++++++- go.mod | 2 + go.sum | 1 + 3 files changed, 158 insertions(+), 2 deletions(-) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 18016d71..69bcc8f8 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -1,17 +1,29 @@ package cmd import ( + "encoding/csv" + "encoding/json" "fmt" + "github.com/btcsuite/btcutil/bech32" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/spf13/cobra" "github.com/tendermint/tendermint/types" + "log" + "os" ) const ( + ReconcilliationDataPath = "reconciliation_data.csv" + ReconciliationAccAddress = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x" /* "asi1rhrlzsx9z865dqen8t4v47r99dw6y4vaw76rd9" */ + flagNewDescription = "new-description" Bech32Chars = "023456789acdefghjklmnpqrstuvwxyz" AddrDataLength = 32 @@ -54,13 +66,18 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { genFile := config.GenesisFile() - _, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) if err != nil { return fmt.Errorf("failed to unmarshal genesis state: %w", err) } // replace chain-id ASIGenesisUpgradeReplaceChainID(genDoc) + + if err = ASIGenesisUpgradeWithdrawReconciliationBalances(clientCtx.Codec, &appState); err != nil { + return fmt.Errorf("failed to withdraw reconciliation balances: %w", err) + } + return genutil.ExportGenesisFile(genDoc, genFile) }, } @@ -85,4 +102,140 @@ func ASIGenesisUpgradeReplaceAddresses() {} func ASIGenesisUpgradeWithdrawIBCChannelsBalances() {} -func ASIGenesisUpgradeWithdrawReconciliationBalances() {} +func getGenesisAccountsMap(authGenState *authtypes.GenesisState) (map[string]*authtypes.GenesisAccount, *authtypes.GenesisAccounts, error) { + accountMap := make(map[string]*authtypes.GenesisAccount) + + accounts, err := authtypes.UnpackAccounts(authGenState.Accounts) + if err != nil { + return nil, nil, fmt.Errorf("failed to unpack accounts from authtypes.GenState: %w", err) + } + + for _, account := range accounts { + // ensure account is valid + err := account.Validate() + if err != nil { + return nil, nil, err + } + + accountMap[account.GetAddress().String()] = &account + } + + return accountMap, &accounts, nil +} + +func getGenesisBalancesMap(bankGenState *banktypes.GenesisState) (*map[string]int, error) { + balanceMap := make(map[string]int) + + for i, balance := range bankGenState.Balances { + balanceMap[balance.Address] = i + } + + return &balanceMap, nil +} + +func ASIGenesisUpgradeWithdrawReconciliationBalances(cdc codec.Codec, appState *map[string]json.RawMessage) error { + bankGenState := banktypes.GetGenesisStateFromAppState(cdc, *appState) + balances := bankGenState.Balances + balanceMap, err := getGenesisBalancesMap(bankGenState) + if err != nil { + return err + } + + authGenState := authtypes.GetGenesisStateFromAppState(cdc, *appState) + accountMap, _, err := getGenesisAccountsMap(&authGenState) + if err != nil { + return err + } + + file, err := os.Open(ReconcilliationDataPath) + if err != nil { + log.Fatalf("Error opening reconciliation data: %s", err) + } + defer file.Close() + + r := csv.NewReader(file) + items, err := r.ReadAll() + if err != nil { + log.Fatalf("Error reading reconciliation data: %s", err) + } + + //reconciliationAccount, ok := accountMap[ReconciliationAccAddress] + //if !ok { + // return fmt.Errorf("no genesis match for reconciliation address: %s", ReconciliationAccAddress) + //} + + reconciliationBalanceIdx, ok := (*balanceMap)[ReconciliationAccAddress] + if !ok { + return fmt.Errorf("no genesis match for reconciliation address: %s", ReconciliationAccAddress) + } + + for _, row := range items { + addr := row[2] + + //_ = row[3] balance from CSV + + // TODO: use this conversion when merging with address replacement branch + //addr, err := convertAddressToASI(oldAddr, AccAddressPrefix) + //if err != nil { + // return fmt.Errorf("failed to convert address: %w", err) + //} + //if _, err = sdk.AccAddressFromBech32(addr); err != nil { + // return fmt.Errorf("converted address is invalid: %w", err) + //} + + acc, ok := accountMap[addr] + if !ok { + return fmt.Errorf("no genesis match for reconciliation address: %s", addr) + } + + balanceIdx, ok := (*balanceMap)[addr] + if !ok { + continue + } + + accBalance := balances[balanceIdx] + + // check if the reconciliation address is still dormant and contains funds + if (*acc).GetSequence() != 0 && !accBalance.Coins.IsAllPositive() { + fmt.Println("Reconciliation address is not dormant or has no funds, skipping withdrawal") + continue + } + + // withdraw funds from the reconciliation address + balances[reconciliationBalanceIdx].Coins = balances[reconciliationBalanceIdx].Coins.Add(accBalance.Coins...) + + // zero out the other account's balance + balances[balanceIdx].Coins = sdk.NewCoins() + } + + // update the bank genesis state + (*bankGenState).Balances = balances + + bankGenStateBytes, err := cdc.MarshalJSON(bankGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + + (*appState)[banktypes.ModuleName] = bankGenStateBytes + + return nil +} + +func convertAddressToASI(addr string, addressPrefix string) (string, error) { + _, decodedAddrData, err := bech32.Decode(addr) + if err != nil { + return "", err + } + + newAddress, err := bech32.Encode(NewAddrPrefix+addressPrefix, decodedAddrData) + if err != nil { + return "", err + } + + err = sdk.VerifyAddressFormat(decodedAddrData) + if err != nil { + return "", err + } + + return newAddress, nil +} diff --git a/go.mod b/go.mod index 32d3950a..b446d809 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,8 @@ require ( github.com/tendermint/tm-db v0.6.7 ) +require github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce + require ( filippo.io/edwards25519 v1.0.0-beta.2 // indirect github.com/99designs/keyring v1.1.6 // indirect diff --git a/go.sum b/go.sum index a83afb2d..6467bd8d 100644 --- a/go.sum +++ b/go.sum @@ -122,6 +122,7 @@ github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+q github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= From cbb770d571dd35cffb4549af0b04aa1406c39311 Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Thu, 18 Apr 2024 12:41:37 +0100 Subject: [PATCH 2/3] feat: embed reconciliation data --- cmd/fetchd/cmd/genasiupgrade.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 69bcc8f8..19019cb0 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -1,6 +1,8 @@ package cmd import ( + "bytes" + _ "embed" "encoding/csv" "encoding/json" "fmt" @@ -44,6 +46,9 @@ const ( OldAddrPrefix = "fetch" ) +//go:embed reconciliation_data.csv +var reconciliationData []byte + // ASIGenesisUpgradeCmd returns replace-genesis-values cobra Command. func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ @@ -153,7 +158,8 @@ func ASIGenesisUpgradeWithdrawReconciliationBalances(cdc codec.Codec, appState * } defer file.Close() - r := csv.NewReader(file) + fileData := reconciliationData + r := csv.NewReader(bytes.NewReader(fileData)) items, err := r.ReadAll() if err != nil { log.Fatalf("Error reading reconciliation data: %s", err) From cfba0010f0335c1c069dc487737d6233096366cf Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Thu, 18 Apr 2024 14:29:29 +0100 Subject: [PATCH 3/3] fix: reflect changes in genesis.json & remove file opening --- cmd/fetchd/cmd/genasiupgrade.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 19019cb0..f358089c 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -19,7 +19,6 @@ import ( "github.com/spf13/cobra" "github.com/tendermint/tendermint/types" "log" - "os" ) const ( @@ -83,6 +82,13 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { return fmt.Errorf("failed to withdraw reconciliation balances: %w", err) } + // reflect changes in the genesis file + var modifiedGenState json.RawMessage + if modifiedGenState, err = json.Marshal(appState); err != nil { + return fmt.Errorf("failed to marshal app state: %w", err) + } + + (*genDoc).AppState = modifiedGenState return genutil.ExportGenesisFile(genDoc, genFile) }, } @@ -152,12 +158,6 @@ func ASIGenesisUpgradeWithdrawReconciliationBalances(cdc codec.Codec, appState * return err } - file, err := os.Open(ReconcilliationDataPath) - if err != nil { - log.Fatalf("Error opening reconciliation data: %s", err) - } - defer file.Close() - fileData := reconciliationData r := csv.NewReader(bytes.NewReader(fileData)) items, err := r.ReadAll()