From 936982a8c55afc9051ba2ae8c64a8a9d9b74067a Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Mon, 13 May 2024 18:59:40 +0100 Subject: [PATCH 1/7] feat: initial mobix staking replacement function --- cmd/fetchd/cmd/genasiupgrade.go | 74 +++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 03be8764..c8d3718f 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -3,7 +3,9 @@ package cmd import ( "bytes" _ "embed" + "encoding/base64" "encoding/csv" + "encoding/hex" "encoding/json" "fmt" "github.com/btcsuite/btcutil/bech32" @@ -29,6 +31,8 @@ const ( BridgeContractAddress = "fetch1qxxlalvsdjd07p07y3rc5fu6ll8k4tmetpha8n" NewBridgeContractAdmin = "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw" + MobixStakingContractAddress = "fetch1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3szdul6e" + IbcWithdrawAddress = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x" /* "asi1rhrlzsx9z865dqen8t4v47r99dw6y4vaw76rd9" */ ReconciliationWithdrawAddress = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x" @@ -96,6 +100,9 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { // replace bridge contract admin ASIGenesisUpgradeReplaceBridgeAdmin(jsonData) + // update mobix staking contract + ASIGenesisUpgradeUpdateMobixStakingContract(jsonData) + // withdraw balances from IBC channels if err = ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData); err != nil { return err @@ -132,6 +139,73 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { return cmd } +func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{}) { + contracts := jsonData["wasm"].(map[string]interface{})["contracts"].([]interface{}) + + re := regexp.MustCompile(fmt.Sprintf(`%s%s1([%s]{%d})$`, OldAddrPrefix, "", Bech32Chars, AddrDataLength+AddrChecksumLength)) + + for _, contract := range contracts { + if contract.(map[string]interface{})["contract_address"] == MobixStakingContractAddress { + mobixContractStates := contract.(map[string]interface{})["contract_state"].([]interface{}) + for _, val := range mobixContractStates { + state := val.(map[string]interface{}) + hexKey := state["key"].(string) + b64Value := state["value"].(string) + + keyBytes, err := hex.DecodeString(hexKey) + if err != nil { + panic(err) + } + + valueBytes, err := base64.StdEncoding.DecodeString(b64Value) + if err != nil { + panic(err) + } + + val = replaceContractState(re, string(keyBytes), string(valueBytes)) + } + + return + } + } + + panic("mobix staking contract not found") +} + +func replaceContractState(re *regexp.Regexp, key string, value string) map[string]interface{} { + var newKey []byte + var newValue []byte + + // replace key + newKeyStr := re.ReplaceAllStringFunc(key, func(match string) string { + newAddr, err := convertAddressToASI(match, AccAddressPrefix) + if err != nil { + panic(err) + } + return newAddr + }) + newKey = []byte(newKeyStr) + + // replace value + valJson := make(map[string]interface{}) + if err := json.Unmarshal([]byte(value), &valJson); err != nil { + panic(err) + } + + var err error + replaceAddresses(AccAddressPrefix, valJson, AddrDataLength+AddrChecksumLength) + newValue, err = json.Marshal(valJson) + if err != nil { + panic(err) + } + + // return reconstructed contract state + return map[string]interface{}{ + "key": hex.EncodeToString(newKey), + "value": base64.StdEncoding.EncodeToString(newValue), + } +} + func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}) { type jsonMap map[string]interface{} From ff31038b113a741ba3ebe33e614ecff1e86c501c Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Thu, 16 May 2024 12:24:16 +0100 Subject: [PATCH 2/7] chore: add support for testnet --- cmd/fetchd/cmd/genasiupgrade.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 4a1dfdf5..0ae49262 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -55,6 +55,9 @@ var networkInfos = map[string]NetworkConfig{ IbcTargetAddr: "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x", // TODO(JS): amend this ReconciliationTargetAddr: &ReconciliationTargetAddr, // TODO(JS): amend this Contracts: &Contracts{ + MobixStaking: &MobixStaking{ + Addr: "fetch1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3szdul6e", // TODO(JS): amend this + }, TokenBridge: &TokenBridge{ Addr: "fetch1qxxlalvsdjd07p07y3rc5fu6ll8k4tmetpha8n", NewAdmin: "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw", @@ -71,6 +74,11 @@ var networkInfos = map[string]NetworkConfig{ OldDenom: "atestfet", }, IbcTargetAddr: "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x", // TODO(JS): amend this + Contracts: &Contracts{ + MobixStaking: &MobixStaking{ + Addr: "fetch1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3szdul6e", + }, + }, }, } @@ -122,13 +130,15 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { // replace chain-id ASIGenesisUpgradeReplaceChainID(genDoc, networkConfig) - // replace bridge contract admin + // replace bridge contract admin, if address and new admin present if networkConfig.Contracts != nil && networkConfig.Contracts.TokenBridge != nil { ASIGenesisUpgradeReplaceBridgeAdmin(jsonData, networkConfig) } - // update mobix staking contract - ASIGenesisUpgradeUpdateMobixStakingContract(jsonData) + // update mobix staking contract, if address present + if networkConfig.Contracts != nil && networkConfig.Contracts.MobixStaking != nil { + ASIGenesisUpgradeUpdateMobixStakingContract(jsonData, networkConfig) + } // withdraw balances from IBC channels if err = ASIGenesisUpgradeWithdrawIBCChannelsBalances(jsonData, networkConfig); err != nil { @@ -168,8 +178,9 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { return cmd } -func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{}) { +func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{}, networkInfo NetworkConfig) { contracts := jsonData["wasm"].(map[string]interface{})["contracts"].([]interface{}) + MobixStakingContractAddress := networkInfo.Contracts.MobixStaking.Addr re := regexp.MustCompile(fmt.Sprintf(`%s%s1([%s]{%d})$`, OldAddrPrefix, "", Bech32Chars, AddrDataLength+AddrChecksumLength)) @@ -584,10 +595,15 @@ type DenomInfo struct { } type Contracts struct { - TokenBridge *TokenBridge + TokenBridge *TokenBridge + MobixStaking *MobixStaking } type TokenBridge struct { Addr string NewAdmin string } + +type MobixStaking struct { + Addr string +} From 0bb35a5934c138de88b2a79c5ac72171ed06893c Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Mon, 20 May 2024 16:30:52 +0100 Subject: [PATCH 3/7] fix: remove restrictive address regex & split state modifier func --- cmd/fetchd/cmd/genasiupgrade.go | 48 ++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/cmd/fetchd/cmd/genasiupgrade.go b/cmd/fetchd/cmd/genasiupgrade.go index 0ae49262..3b027857 100644 --- a/cmd/fetchd/cmd/genasiupgrade.go +++ b/cmd/fetchd/cmd/genasiupgrade.go @@ -31,6 +31,7 @@ const ( Bech32Chars = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" AddrDataLength = 32 WasmAddrDataLength = 52 + MaxAddrDataLength = 100 AddrChecksumLength = 6 AccAddressPrefix = "" @@ -182,7 +183,7 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} contracts := jsonData["wasm"].(map[string]interface{})["contracts"].([]interface{}) MobixStakingContractAddress := networkInfo.Contracts.MobixStaking.Addr - re := regexp.MustCompile(fmt.Sprintf(`%s%s1([%s]{%d})$`, OldAddrPrefix, "", Bech32Chars, AddrDataLength+AddrChecksumLength)) + re := regexp.MustCompile(fmt.Sprintf(`%s%s1([%s]{%d,%d})$`, OldAddrPrefix, "", Bech32Chars, AddrDataLength+AddrChecksumLength, MaxAddrDataLength)) for _, contract := range contracts { if contract.(map[string]interface{})["contract_address"] == MobixStakingContractAddress { @@ -202,7 +203,13 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} panic(err) } - val = replaceContractState(re, string(keyBytes), string(valueBytes)) + updatedKey := replaceContractStateKey(re, string(keyBytes)) + updatedValue := replaceContractStateValue(re, string(valueBytes)) + + val = map[string]interface{}{ + "key": updatedKey, + "value": updatedValue, + } } return @@ -212,20 +219,9 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} panic("mobix staking contract not found") } -func replaceContractState(re *regexp.Regexp, key string, value string) map[string]interface{} { - var newKey []byte +func replaceContractStateValue(re *regexp.Regexp, value string) string { var newValue []byte - // replace key - newKeyStr := re.ReplaceAllStringFunc(key, func(match string) string { - newAddr, err := convertAddressToASI(match, AccAddressPrefix) - if err != nil { - panic(err) - } - return newAddr - }) - newKey = []byte(newKeyStr) - // replace value valJson := make(map[string]interface{}) if err := json.Unmarshal([]byte(value), &valJson); err != nil { @@ -239,11 +235,25 @@ func replaceContractState(re *regexp.Regexp, key string, value string) map[strin panic(err) } - // return reconstructed contract state - return map[string]interface{}{ - "key": hex.EncodeToString(newKey), - "value": base64.StdEncoding.EncodeToString(newValue), - } + // return new value + return base64.StdEncoding.EncodeToString(newValue) +} + +func replaceContractStateKey(re *regexp.Regexp, key string) string { + var newKey []byte + + // replace key + newKeyStr := re.ReplaceAllStringFunc(key, func(match string) string { + newAddr, err := convertAddressToASI(match, AccAddressPrefix) + if err != nil { + panic(err) + } + return newAddr + }) + newKey = []byte(newKeyStr) + + // return new key + return hex.EncodeToString(newKey) } func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}, networkInfo NetworkConfig) { From 37438e50862552e9411149b417addc7f1d5acf79 Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Tue, 21 May 2024 14:59:04 +0100 Subject: [PATCH 4/7] fix: stop removing state key prefix --- cmd/fetchd/cmd/genesis-asi-upgrade.go | 63 ++++++++++++++++++++------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/cmd/fetchd/cmd/genesis-asi-upgrade.go b/cmd/fetchd/cmd/genesis-asi-upgrade.go index d9a700f9..391e2d25 100644 --- a/cmd/fetchd/cmd/genesis-asi-upgrade.go +++ b/cmd/fetchd/cmd/genesis-asi-upgrade.go @@ -42,6 +42,12 @@ const ( OldAddrPrefix = "fetch" ) +var ( + stakesKey = lengthPrefixStr("stakes") + unbondEntriesKey = lengthPrefixStr("unbond_entries") + configKey = []byte("config") +) + var ReconciliationTargetAddr = "fetch1rhrlzsx9z865dqen8t4v47r99dw6y4va4uph0x" var networkInfos = map[string]NetworkConfig{ @@ -236,18 +242,22 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} hexKey := state["key"].(string) b64Value := state["value"].(string) - keyBytes, err := hex.DecodeString(hexKey) + valueBytes, err := base64.StdEncoding.DecodeString(b64Value) if err != nil { panic(err) } - valueBytes, err := base64.StdEncoding.DecodeString(b64Value) + updatedValue := b64Value + keyBytes, err := hex.DecodeString(hexKey) if err != nil { panic(err) } - updatedKey := replaceContractStateKey(re, string(keyBytes)) - updatedValue := replaceContractStateValue(re, string(valueBytes)) + updatedKey := replaceContractStateKey(re, keyBytes) + + if bytes.Compare(keyBytes, configKey) == 0 { + updatedValue = replaceContractStateValue(re, string(valueBytes)) + } val = map[string]interface{}{ "key": updatedKey, @@ -272,7 +282,7 @@ func replaceContractStateValue(re *regexp.Regexp, value string) string { } var err error - replaceAddresses(AccAddressPrefix, valJson, AddrDataLength+AddrChecksumLength) + replaceAddresses(AccAddressPrefix, valJson, MaxAddrDataLength) newValue, err = json.Marshal(valJson) if err != nil { panic(err) @@ -282,18 +292,39 @@ func replaceContractStateValue(re *regexp.Regexp, value string) string { return base64.StdEncoding.EncodeToString(newValue) } -func replaceContractStateKey(re *regexp.Regexp, key string) string { - var newKey []byte +func lengthPrefixStr(val string) []byte { + length := len(val) + + if length > 0xFF { + panic("length of input string is greater than two bytes") + } + + byteArray := []byte("00" + val) + + byteArray[0] = byte((0xFF00 & length) >> 8) + byteArray[1] = byte(0x00FF & length) + + return byteArray +} - // replace key - newKeyStr := re.ReplaceAllStringFunc(key, func(match string) string { - newAddr, err := convertAddressToASI(match, AccAddressPrefix) +func replaceContractStateKey(re *regexp.Regexp, keyBytes []byte) string { + replaceKey := func(prefix []byte) []byte { + // replace key + newAddr, err := convertAddressToASI(string(keyBytes[len(prefix):]), AccAddressPrefix) if err != nil { panic(err) } - return newAddr - }) - newKey = []byte(newKeyStr) + return append(prefix, []byte(newAddr)...) + } + + var newKey []byte + if bytes.Compare(keyBytes[0:len(stakesKey)], stakesKey) == 0 { + newKey = replaceKey(stakesKey) + } else if bytes.Compare(keyBytes[0:len(unbondEntriesKey)], unbondEntriesKey) == 0 { + newKey = replaceKey(unbondEntriesKey) + } else { + return hex.EncodeToString(keyBytes) + } // return new key return hex.EncodeToString(newKey) @@ -740,9 +771,9 @@ type DenomInfo struct { } type Contracts struct { - TokenBridge *TokenBridge - Almanac *Almanac - AName *AName + TokenBridge *TokenBridge + Almanac *Almanac + AName *AName MobixStaking *MobixStaking } From b2fc45e22680e1978a0eb0396f2e57e7331d41ec Mon Sep 17 00:00:00 2001 From: Peter Bukva Date: Wed, 22 May 2024 13:17:03 +0100 Subject: [PATCH 5/7] Refactoring mobix contract upgrade --- cmd/fetchd/cmd/genesis-asi-upgrade.go | 62 +++++++++++++-------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/cmd/fetchd/cmd/genesis-asi-upgrade.go b/cmd/fetchd/cmd/genesis-asi-upgrade.go index 391e2d25..c247e8cc 100644 --- a/cmd/fetchd/cmd/genesis-asi-upgrade.go +++ b/cmd/fetchd/cmd/genesis-asi-upgrade.go @@ -4,6 +4,7 @@ import ( "bytes" _ "embed" "encoding/base64" + "encoding/binary" "encoding/csv" "encoding/hex" "encoding/json" @@ -43,8 +44,8 @@ const ( ) var ( - stakesKey = lengthPrefixStr("stakes") - unbondEntriesKey = lengthPrefixStr("unbond_entries") + stakesKey = prefixStringWithLength("stakes") + unbondEntriesKey = prefixStringWithLength("unbond_entries") configKey = []byte("config") ) @@ -228,6 +229,12 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { return cmd } +type Bytes []byte + +func (a Bytes) StartsWith(with []byte) bool { + return len(a) >= len(with) && bytes.Compare(a[0:len(stakesKey)], with) == 0 +} + func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{}, networkInfo NetworkConfig) { contracts := jsonData["wasm"].(map[string]interface{})["contracts"].([]interface{}) MobixStakingContractAddress := networkInfo.Contracts.MobixStaking.Addr @@ -247,16 +254,22 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} panic(err) } + updatedKey := hexKey updatedValue := b64Value + keyBytes, err := hex.DecodeString(hexKey) if err != nil { panic(err) } - updatedKey := replaceContractStateKey(re, keyBytes) - - if bytes.Compare(keyBytes, configKey) == 0 { - updatedValue = replaceContractStateValue(re, string(valueBytes)) + _keyBytes := Bytes(keyBytes) + switch { + case _keyBytes.StartsWith(stakesKey): + updatedKey = replaceAddressInContractStateKey(keyBytes, stakesKey) + case _keyBytes.StartsWith(unbondEntriesKey): + updatedKey = replaceAddressInContractStateKey(keyBytes, unbondEntriesKey) + case _keyBytes.StartsWith(configKey): + updatedValue = replaceAddressInContractStateValue(re, string(valueBytes)) } val = map[string]interface{}{ @@ -272,7 +285,7 @@ func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{} panic("mobix staking contract not found") } -func replaceContractStateValue(re *regexp.Regexp, value string) string { +func replaceAddressInContractStateValue(re *regexp.Regexp, value string) string { var newValue []byte // replace value @@ -292,42 +305,27 @@ func replaceContractStateValue(re *regexp.Regexp, value string) string { return base64.StdEncoding.EncodeToString(newValue) } -func lengthPrefixStr(val string) []byte { +func prefixStringWithLength(val string) []byte { length := len(val) - if length > 0xFF { - panic("length of input string is greater than two bytes") + if length > 0xFFFF { + panic("length of input string does fit in to uint16") } byteArray := []byte("00" + val) - - byteArray[0] = byte((0xFF00 & length) >> 8) - byteArray[1] = byte(0x00FF & length) + binary.BigEndian.PutUint16(byteArray, uint16(length)) return byteArray } -func replaceContractStateKey(re *regexp.Regexp, keyBytes []byte) string { - replaceKey := func(prefix []byte) []byte { - // replace key - newAddr, err := convertAddressToASI(string(keyBytes[len(prefix):]), AccAddressPrefix) - if err != nil { - panic(err) - } - return append(prefix, []byte(newAddr)...) - } - - var newKey []byte - if bytes.Compare(keyBytes[0:len(stakesKey)], stakesKey) == 0 { - newKey = replaceKey(stakesKey) - } else if bytes.Compare(keyBytes[0:len(unbondEntriesKey)], unbondEntriesKey) == 0 { - newKey = replaceKey(unbondEntriesKey) - } else { - return hex.EncodeToString(keyBytes) +func replaceAddressInContractStateKey(keyBytes []byte, prefix []byte) string { + newAddr, err := convertAddressToASI(string(keyBytes[len(prefix):]), AccAddressPrefix) + if err != nil { + panic(err) } + key := append(prefix, []byte(newAddr)...) - // return new key - return hex.EncodeToString(newKey) + return hex.EncodeToString(key) } func ASIGenesisUpgradeReplaceDenomMetadata(jsonData map[string]interface{}, networkInfo NetworkConfig) { From 2a4f7509be385411c3cdb3e4846406bbc3a9245c Mon Sep 17 00:00:00 2001 From: Peter Bukva Date: Wed, 22 May 2024 13:21:51 +0100 Subject: [PATCH 6/7] Fixing usage of wrong variable in `Bytes.StartsWith(...)` --- cmd/fetchd/cmd/genesis-asi-upgrade.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/fetchd/cmd/genesis-asi-upgrade.go b/cmd/fetchd/cmd/genesis-asi-upgrade.go index c247e8cc..91f4357b 100644 --- a/cmd/fetchd/cmd/genesis-asi-upgrade.go +++ b/cmd/fetchd/cmd/genesis-asi-upgrade.go @@ -232,7 +232,7 @@ func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { type Bytes []byte func (a Bytes) StartsWith(with []byte) bool { - return len(a) >= len(with) && bytes.Compare(a[0:len(stakesKey)], with) == 0 + return len(a) >= len(with) && bytes.Compare(a[0:len(with)], with) == 0 } func ASIGenesisUpgradeUpdateMobixStakingContract(jsonData map[string]interface{}, networkInfo NetworkConfig) { From 77ed489a1c69e69fb649fc80e2636846c0ec8180 Mon Sep 17 00:00:00 2001 From: jonathansumner Date: Wed, 22 May 2024 19:06:55 +0100 Subject: [PATCH 7/7] fix: amend address replacement regex --- cmd/fetchd/cmd/genesis-asi-upgrade.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/fetchd/cmd/genesis-asi-upgrade.go b/cmd/fetchd/cmd/genesis-asi-upgrade.go index 91f4357b..de54931b 100644 --- a/cmd/fetchd/cmd/genesis-asi-upgrade.go +++ b/cmd/fetchd/cmd/genesis-asi-upgrade.go @@ -295,7 +295,7 @@ func replaceAddressInContractStateValue(re *regexp.Regexp, value string) string } var err error - replaceAddresses(AccAddressPrefix, valJson, MaxAddrDataLength) + replaceAddresses(AccAddressPrefix, valJson, AddrDataLength+AddrChecksumLength) newValue, err = json.Marshal(valJson) if err != nil { panic(err)