-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
18ddc93
commit 75d7e2f
Showing
3 changed files
with
263 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/btcsuite/btcutil/bech32" | ||
"github.com/cosmos/cosmos-sdk/codec" | ||
cosmostypes "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/tendermint/tendermint/types" | ||
"regexp" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/server" | ||
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" | ||
) | ||
|
||
const ( | ||
flagNewDescription = "new-description" | ||
Bech32Chars = "023456789acdefghjklmnpqrstuvwxyz" | ||
AddrDataLength = 32 | ||
AddrChecksumLength = 6 | ||
AccAddressPrefix = "" | ||
ValAddressPrefix = "valoper" | ||
ConsAddressPrefix = "valcons" | ||
|
||
NewBaseDenom = "asi" | ||
NewDenom = "aasi" | ||
NewAddrPrefix = "asi" | ||
NewChainId = "asi-1" | ||
NewDescription = "ASI Token" | ||
|
||
OldBaseDenom = "fet" | ||
OldDenom = "afet" | ||
OldAddrPrefix = "fetch" | ||
) | ||
|
||
//" fetch char* [bech32 chars] limited by size " CHECKSUM? | ||
//verify functions after! | ||
|
||
// ASIGenesisUpgradeCmd returns replace-genesis-values cobra Command. | ||
func ASIGenesisUpgradeCmd(defaultNodeHome string) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "asi-genesis-upgrade", | ||
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" | ||
- The native coin denom will be updated to "asi" | ||
- The address prefix will be updated to "asi" | ||
- The old fetch addresses will be updated to the new asi addresses`, | ||
|
||
Args: cobra.ExactArgs(0), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
cdc := clientCtx.Codec | ||
|
||
serverCtx := server.GetServerContextFromCmd(cmd) | ||
config := serverCtx.Config | ||
|
||
config.SetRoot(clientCtx.HomeDir) | ||
|
||
genFile := config.GenesisFile() | ||
|
||
appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) | ||
if err != nil { | ||
return fmt.Errorf("failed to unmarshal genesis state: %w", err) | ||
} | ||
|
||
// replace chain-id | ||
ASIGenesisUpgradeReplaceChainID(genDoc) | ||
|
||
// set denom metadata in bank module | ||
err = ASIGenesisUpgradeReplaceDenomMetadata(cdc, &appState) | ||
if err != nil { | ||
return fmt.Errorf("failed to replace denom metadata: %w", err) | ||
} | ||
|
||
appStateJSON, err := json.Marshal(appState) | ||
if err != nil { | ||
return fmt.Errorf("failed to marshal application genesis state: %w", err) | ||
} | ||
|
||
appStateStr := string(appStateJSON) | ||
|
||
// replace denom across the genesis file | ||
ASIGenesisUpgradeReplaceDenom(&appStateStr) | ||
|
||
// replace addresses across the genesis file | ||
ASIGenesisUpgradeReplaceAddresses(&appStateStr) | ||
|
||
genDoc.AppState = []byte(appStateStr) | ||
return genutil.ExportGenesisFile(genDoc, genFile) | ||
}, | ||
} | ||
|
||
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(flagNewDescription, "", "The new description for the native coin in the genesis file") | ||
flags.AddQueryFlagsToCmd(cmd) | ||
|
||
return cmd | ||
} | ||
|
||
func ASIGenesisUpgradeReplaceDenomMetadata(cdc codec.Codec, appState *map[string]json.RawMessage) error { | ||
bankGenState := banktypes.GetGenesisStateFromAppState(cdc, *appState) | ||
|
||
denomRegex := getRegex(OldDenom, NewDenom) | ||
upperDenomRegex := getRegex(strings.ToUpper(OldBaseDenom), strings.ToUpper(NewBaseDenom)) | ||
exponentDenomRegex := getPartialRegexLeft(OldBaseDenom, NewBaseDenom) | ||
|
||
for _, metadata := range bankGenState.DenomMetadata { | ||
replaceString(&metadata.Base, []*regexPair{denomRegex}) | ||
if metadata.Name == OldBaseDenom { | ||
metadata.Description = NewDescription | ||
} | ||
replaceString(&metadata.Display, []*regexPair{upperDenomRegex}) | ||
replaceString(&metadata.Name, []*regexPair{upperDenomRegex}) | ||
replaceString(&metadata.Symbol, []*regexPair{upperDenomRegex}) | ||
for _, unit := range metadata.DenomUnits { | ||
replaceString(&unit.Denom, []*regexPair{upperDenomRegex}) | ||
replaceString(&unit.Denom, []*regexPair{exponentDenomRegex}) | ||
} | ||
|
||
} | ||
|
||
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 ASIGenesisUpgradeReplaceChainID(genesisData *types.GenesisDoc) { | ||
genesisData.ChainID = NewChainId | ||
} | ||
|
||
func ASIGenesisUpgradeReplaceDenom(jsonString *string) { | ||
for _, target := range []string{"denom", "bond_denom", "mint_denom", "base_denom", "base"} { | ||
re := regexp.MustCompile(fmt.Sprintf(`("%s"\s*:\s*)"%s"`, target, OldDenom)) | ||
if re.MatchString(*jsonString) { | ||
*jsonString = re.ReplaceAllString(*jsonString, fmt.Sprintf(`${1}"%s"`, NewDenom)) | ||
} | ||
} | ||
} | ||
|
||
func ASIGenesisUpgradeReplaceAddresses(jsonString *string) { | ||
replaceAddresses(AccAddressPrefix, jsonString) | ||
replaceAddresses(ValAddressPrefix, jsonString) | ||
replaceAddresses(ConsAddressPrefix, jsonString) | ||
} | ||
|
||
type AddressInfo struct { | ||
Original string | ||
Modified string | ||
} | ||
|
||
func replaceAddresses(addressTypePrefix string, jsonString *string) { | ||
re := regexp.MustCompile(fmt.Sprintf(`"%s%s1([%s]{%d})"`, OldAddrPrefix, addressTypePrefix, Bech32Chars, AddrDataLength+AddrChecksumLength)) | ||
matches := re.FindAllString(*jsonString, -1) | ||
|
||
addressChan := make(chan AddressInfo, len(matches)) | ||
|
||
var wg sync.WaitGroup | ||
wg.Add(len(matches)) | ||
|
||
for _, match := range matches { | ||
go func(match string) { | ||
defer wg.Done() | ||
|
||
matchedAddr := strings.ReplaceAll(match, `"`, "") | ||
_, decodedAddrData, err := bech32.Decode(matchedAddr) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
newAddress, err := bech32.Encode(NewAddrPrefix+addressTypePrefix, decodedAddrData) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
err = cosmostypes.VerifyAddressFormat(decodedAddrData) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
switch addressTypePrefix { | ||
case AccAddressPrefix: | ||
_, err = cosmostypes.AccAddressFromBech32(newAddress) | ||
case ValAddressPrefix: | ||
_, err = cosmostypes.ValAddressFromBech32(newAddress) | ||
case ConsAddressPrefix: | ||
_, err = cosmostypes.ConsAddressFromBech32(newAddress) | ||
default: | ||
panic("invalid address type prefix") | ||
} | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
addressChan <- AddressInfo{Original: matchedAddr, Modified: newAddress} | ||
}(match) | ||
} | ||
|
||
go func() { | ||
wg.Wait() | ||
close(addressChan) | ||
}() | ||
|
||
modifiedAddresses := make(map[string]string) | ||
for addressInfo := range addressChan { | ||
modifiedAddresses[addressInfo.Original] = addressInfo.Modified | ||
} | ||
|
||
//TODO: This is not feasible time-wise - we need to come up with a quicker method | ||
for originalAddr, modifiedAddr := range modifiedAddresses { | ||
*jsonString = strings.ReplaceAll(*jsonString, originalAddr, fmt.Sprintf(`"%s"`, modifiedAddr)) | ||
} | ||
} | ||
|
||
func ASIGenesisUpgradeWithdrawIBCChannelsBalances() { | ||
|
||
} | ||
|
||
func ASIGenesisUpgradeWithdrawReconciliationBalances() { | ||
|
||
} | ||
|
||
func getRegex(oldValue string, newValue string) *regexPair { | ||
return ®exPair{ | ||
pattern: fmt.Sprintf(`^%s$`, oldValue), | ||
replacement: fmt.Sprintf(`%s`, newValue), | ||
} | ||
} | ||
|
||
func getPartialRegexLeft(oldValue string, newValue string) *regexPair { | ||
return ®exPair{ | ||
pattern: fmt.Sprintf(`(.*?)%s"`, oldValue), | ||
replacement: fmt.Sprintf(`${1}%s"`, newValue), | ||
} | ||
} | ||
|
||
func replaceString(s *string, replacements []*regexPair) { | ||
for _, pair := range replacements { | ||
re := regexp.MustCompile(pair.pattern) | ||
if re.MatchString(*s) { | ||
*s = re.ReplaceAllString(*s, pair.replacement) | ||
} | ||
} | ||
} | ||
|
||
type regexPair struct { | ||
pattern string | ||
replacement string | ||
} |
Oops, something went wrong.