Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: testnet support #349

Merged
merged 3 commits into from
May 16, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 116 additions & 50 deletions cmd/fetchd/cmd/genasiupgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ import (
)

const (
BridgeContractAddress = "fetch1qxxlalvsdjd07p07y3rc5fu6ll8k4tmetpha8n"
NewBridgeContractAdmin = "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw"

IbcWithdrawAddress = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x" /* "asi1rhrlzsx9z865dqen8t4v47r99dw6y4vaw76rd9" */
ReconciliationWithdrawAddress = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x"

Bech32Chars = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
AddrDataLength = 32
WasmAddrDataLength = 52
Expand All @@ -41,23 +35,54 @@ const (
ValAddressPrefix = "valoper"
ConsAddressPrefix = "valcons"

NewBaseDenom = "asi"
NewDenom = "aasi"
NewAddrPrefix = "asi"
NewChainId = "asi-1"
NewDescription = "ASI Token" // TODO(JS): change this, potentially

OldDenom = "afet"
NewAddrPrefix = "asi"
OldAddrPrefix = "fetch"
)

var networkInfos = map[string]NetworkConfig{
"mainnet": {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you suggested during the discussion, the key (the "mainnet" in this particular example) should have value of original chain-id from input genesis.json (e.g. fetchhub-4 in this particular case).

NewChainID: "asi-1",
NewDescription: "ASI token", // TODO(JS): confirm this
DenomInfo: DenomInfo{
NewBaseDenom: "asi",
NewDenom: "aasi",
OldDenom: "afet",
},
IbcTargetAddr: "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x", // TODO(JS): amend this
ReconciliationTargetAddr: "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x", // TODO(JS): amend this
Contracts: Contracts{
TokenBridge: TokenBridge{
Addr: "fetch1qxxlalvsdjd07p07y3rc5fu6ll8k4tmetpha8n",
NewAdmin: "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw",
},
},
},
"testnet": {
NewChainID: "dorado-1", // TODO(JS): confirm this
NewDescription: "Test ASI token", // TODO(JS): confirm this
DenomInfo: DenomInfo{
NewBaseDenom: "testasi",
NewDenom: "atestasi",
OldDenom: "atestfet",
},
IbcTargetAddr: "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x", // TODO(JS): amend this
ReconciliationTargetAddr: "", // Not applicable to testnet
Contracts: Contracts{
TokenBridge: TokenBridge{
Addr: "fetch1qxxlalvsdjd07p07y3rc5fu6ll8k4tmetpha8n", // TODO(JS): amend this
NewAdmin: "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw", // TODO(JS): amend this
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testnet, there is no bridge contract = the function handling bridge contract should not be executed. Thei saso means that TokenBridge member in the structure should be pointer so it is not necessary to be set.

},
},
}

//go:embed reconciliation_data.csv
var reconciliationData []byte

// ASIGenesisUpgradeCmd returns replace-genesis-values cobra Command.
func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "asi-genesis-upgrade",
Use: "asi-genesis-upgrade [testnet|mainnet]",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop the [testnet|mainnet] paremeter (the decission wil be made automatically based on the chain-id value in the input genesis.json file.

Short: "This command carries out a full upgrade of the genesis file to the new ASI chain parameters.",
Long: `The following command will upgrade the current genesis file to the new ASI chain parameters. The following changes will be made:
- Chain ID will be updated to "asi-1"
Expand All @@ -70,7 +95,7 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command {
- The reconciliation withdrawal address will be updated to the new address
`,

Args: cobra.ExactArgs(0),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
serverCtx := server.GetServerContextFromCmd(cmd)
Expand All @@ -85,35 +110,43 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command {
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
}

var ok bool
var networkConfig NetworkConfig // TODO(JS): potentially just read Chain-ID, instead of taking a new arg
if networkConfig, ok = networkInfos[args[0]]; !ok {
return fmt.Errorf("network type not found")
}

var jsonData map[string]interface{}
if err = json.Unmarshal(genDoc.AppState, &jsonData); err != nil {
return fmt.Errorf("failed to unmarshal app state: %w", err)
}

// replace chain-id
ASIGenesisUpgradeReplaceChainID(genDoc)
ASIGenesisUpgradeReplaceChainID(genDoc, networkConfig)

// replace bridge contract admin
ASIGenesisUpgradeReplaceBridgeAdmin(jsonData)
ASIGenesisUpgradeReplaceBridgeAdmin(jsonData, networkConfig)

// withdraw balances from IBC channels
if err = ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData); err != nil {
if err = ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData, networkConfig); err != nil {
return err
}

// withdraw balances from reconciliation addresses
if err = ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData); err != nil {
return err
if args[0] == "mainnet" {
if err = ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData, networkConfig); err != nil {
return err
}
}

// set denom metadata in bank module
ASIGenesisUpgradeReplaceDenomMetadata(jsonData)
ASIGenesisUpgradeReplaceDenomMetadata(jsonData, networkConfig)

// replace denom across the genesis file
ASIGenesisUpgradeReplaceDenom(jsonData)
ASIGenesisUpgradeReplaceDenom(jsonData, networkConfig)

// replace addresses across the genesis file
ASIGenesisUpgradeReplaceAddresses(jsonData)
ASIGenesisUpgradeReplaceAddresses(jsonData, networkConfig)

var encodedAppState []byte
if encodedAppState, err = json.Marshal(jsonData); err != nil {
Expand All @@ -126,50 +159,55 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command {
}

cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring backend (os|file|kwallet|pass|test)")
flags.AddQueryFlagsToCmd(cmd)

return cmd
}

func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}) {
func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}, networkInfo NetworkConfig) {
type jsonMap map[string]interface{}

NewBaseDenomUpper := strings.ToUpper(NewBaseDenom)
newBaseDenom := networkInfo.DenomInfo.NewBaseDenom
oldDenom := networkInfo.DenomInfo.OldDenom
newDenom := networkInfo.DenomInfo.NewDenom
newDescription := networkInfo.NewDescription

NewBaseDenomUpper := strings.ToUpper(newBaseDenom)

newMetadata := jsonMap{
"base": NewDenom,
"base": newDenom,
"denom_units": []jsonMap{
{
"denom": NewBaseDenomUpper,
"exponent": 18,
},
{
"denom": fmt.Sprintf("m%s", NewBaseDenom),
"denom": fmt.Sprintf("m%s", newBaseDenom),
"exponent": 15,
},
{
"denom": fmt.Sprintf("u%s", NewBaseDenom),
"denom": fmt.Sprintf("u%s", newBaseDenom),
"exponent": 12,
},
{
"denom": fmt.Sprintf("n%s", NewBaseDenom),
"denom": fmt.Sprintf("n%s", newBaseDenom),
"exponent": 9,
},
{
"denom": fmt.Sprintf("p%s", NewBaseDenom),
"denom": fmt.Sprintf("p%s", newBaseDenom),
"exponent": 6,
},
{
"denom": fmt.Sprintf("f%s", NewBaseDenom),
"denom": fmt.Sprintf("f%s", newBaseDenom),
"exponent": 3,
},
{
"denom": fmt.Sprintf("a%s", NewBaseDenom),
"denom": fmt.Sprintf("a%s", newBaseDenom),
"exponent": 0,
},
},
"description": NewDescription,
"description": newDescription,
"display": NewBaseDenomUpper,
"name": NewBaseDenomUpper,
"symbol": NewBaseDenomUpper,
Expand All @@ -180,46 +218,48 @@ func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}) {

for i, metadata := range denomMetadata {
denomUnit := metadata.(map[string]interface{})
if denomUnit["base"] == OldDenom {
if denomUnit["base"] == oldDenom {
denomMetadata[i] = newMetadata
break
}
}
}

func ASIGenesisUpgradeReplaceChainID(genesisData *types.GenesisDoc) {
genesisData.ChainID = NewChainId
func ASIGenesisUpgradeReplaceChainID(genesisData *types.GenesisDoc, networkInfo NetworkConfig) {
genesisData.ChainID = networkInfo.NewChainID
}

func ASIGenesisUpgradeReplaceBridgeAdmin(jsonData map[string]interface{}) {
func ASIGenesisUpgradeReplaceBridgeAdmin(jsonData map[string]interface{}, networkInfo NetworkConfig) {
contracts := jsonData["wasm"].(map[string]interface{})["contracts"].([]interface{})

for i, contract := range contracts {
c := contract.(map[string]interface{})
if c["contract_address"] == BridgeContractAddress {
if c["contract_address"] == networkInfo.Contracts.TokenBridge.Addr {
contractInfo := c["contract_info"].(map[string]interface{})
contractInfo["admin"] = NewBridgeContractAdmin
contractInfo["admin"] = networkInfo.Contracts.TokenBridge.NewAdmin
contracts[i] = c
break
}
}
}

func ASIGenesisUpgradeReplaceDenom(jsonData map[string]interface{}) {
func ASIGenesisUpgradeReplaceDenom(jsonData map[string]interface{}, networkInfo NetworkConfig) {
targets := map[string]struct{}{"denom": {}, "bond_denom": {}, "mint_denom": {}, "base_denom": {}, "base": {}}
oldDenom := networkInfo.DenomInfo.OldDenom
newDenom := networkInfo.DenomInfo.NewDenom

crawlJson("", jsonData, -1, func(key string, value interface{}, idx int) interface{} {
if str, ok := value.(string); ok {
_, isInTargets := targets[key]
if str == OldDenom && isInTargets {
return NewDenom
if str == oldDenom && isInTargets {
return newDenom
}
}
return value
})
}

func ASIGenesisUpgradeReplaceAddresses(jsonData map[string]interface{}) {
func ASIGenesisUpgradeReplaceAddresses(jsonData map[string]interface{}, networkInfo NetworkConfig) {
// account addresses
replaceAddresses(AccAddressPrefix, jsonData, AddrDataLength+AddrChecksumLength)
// validator addresses
Expand Down Expand Up @@ -249,12 +289,13 @@ func replaceAddresses(addressTypePrefix string, jsonData map[string]interface{},
})
}

func ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData map[string]interface{}) error {
func ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData map[string]interface{}, networkInfo NetworkConfig) error {
bank := jsonData[banktypes.ModuleName].(map[string]interface{})
balances := bank["balances"].([]interface{})
balanceMap := getGenesisBalancesMap(balances)
ibcWithdrawalAddress := networkInfo.IbcTargetAddr

withdrawalBalanceIdx, ok := (*balanceMap)[IbcWithdrawAddress]
withdrawalBalanceIdx, ok := (*balanceMap)[ibcWithdrawalAddress]
if !ok {
fmt.Println("failed to find Ibc withdrawal address in genesis balances - have addresses already been converted?")
return nil
Expand Down Expand Up @@ -319,9 +360,10 @@ func getGenesisAccountSequenceMap(accounts []interface{}) *map[string]int {
return &accountMap
}

func ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData map[string]interface{}) error {
func ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData map[string]interface{}, networkInfo NetworkConfig) error {
bank := jsonData[banktypes.ModuleName].(map[string]interface{})
balances := bank["balances"].([]interface{})
reconciliationWithdrawAddress := networkInfo.ReconciliationTargetAddr

balanceMap := getGenesisBalancesMap(balances)

Expand All @@ -336,9 +378,9 @@ func ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData map[string]interfa
log.Fatalf("Error reading reconciliation data: %s", err)
}

reconciliationBalanceIdx, ok := (*balanceMap)[ReconciliationWithdrawAddress]
reconciliationBalanceIdx, ok := (*balanceMap)[reconciliationWithdrawAddress]
if !ok {
return fmt.Errorf("no genesis match for reconciliation address: %s", ReconciliationWithdrawAddress)
return fmt.Errorf("no match in genesis for reconciliation address: %s", reconciliationWithdrawAddress)
}

for _, row := range items {
Expand All @@ -348,7 +390,7 @@ func ASIGenesisUpgradeWithdrawReconciliationBalances(jsonData map[string]interfa

accSequence, ok := (*accountSequenceMap)[addr]
if !ok {
return fmt.Errorf("no genesis match for reconciliation address: %s", addr)
return fmt.Errorf("no match in genesis for reconciliation address: %s", addr)
}

balanceIdx, ok := (*balanceMap)[addr]
Expand Down Expand Up @@ -455,3 +497,27 @@ func getInterfaceSliceFromCoins(coins sdk.Coins) []interface{} {
}
return balance
}

type NetworkConfig struct {
NewChainID string
NewDescription string
IbcTargetAddr string
ReconciliationTargetAddr string
DenomInfo DenomInfo
Contracts Contracts
}

type DenomInfo struct {
NewBaseDenom string
NewDenom string
OldDenom string
}

type Contracts struct {
TokenBridge TokenBridge
Jonathansumner marked this conversation as resolved.
Show resolved Hide resolved
}

type TokenBridge struct {
Addr string
NewAdmin string
}
Loading