Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Can't use SetSDKContext() more than once #222

Open
freshfab opened this issue Nov 11, 2022 · 2 comments
Open

Can't use SetSDKContext() more than once #222

freshfab opened this issue Nov 11, 2022 · 2 comments

Comments

@freshfab
Copy link

freshfab commented Nov 11, 2022

I have a program that needs to interact with multiple chains. I've given each chain-specific sub-module its own ChainClient & ChainClientConfig to work with, but any time I want to interact with two chains or more (either via query or tx), the second one to get executed uses the Bech32 prefix of the module that was executed first.

The calls to the SDK config did not panic, so it can't be sealed. AFAICS the calls are made each time AccAddress.String() or ValAddress.String() are called, so I tried the following:

done := ChainClient.SetSDKContext()
// Get string representation of the account in the keyring and cache it.
accAddr := key.String()
done()

// Use the 'accAddr' variable from here on instead of calling the String() method each time.

But that didn't work either. I'm not sure how much further down this rabbit hole goes, so maybe someone can enlighten me here.

How To Reproduce

Create two accounts in your local keyring, e.g. one for Cosmos and one for Osmosis.

$ lens keys add cosmos_acc --chain cosmoshub
$ lens keys add osmo_acc --chain osmosis

Then, run this program.

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"

	lens "github.com/strangelove-ventures/lens/client"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	encConfig := zapcore.EncoderConfig{
		MessageKey:	"msg",
		LevelKey:	"level",
		TimeKey:	"time",
		NameKey:	"logger",
		CallerKey:	"caller",
		FunctionKey:	"func",
		StacktraceKey:	"trace",
		LineEnding:	zapcore.DefaultLineEnding,
		EncodeLevel:	zapcore.LowercaseLevelEncoder,
		EncodeTime:	zapcore.ISO8601TimeEncoder,
		EncodeDuration:	zapcore.SecondsDurationEncoder,
		EncodeCaller:	zapcore.ShortCallerEncoder,
	}
	logger := zap.New(
		zapcore.NewCore(zapcore.NewJSONEncoder(encConfig), zapcore.AddSync(os.Stdout), zapcore.InfoLevel),
		zap.AddStacktrace(zapcore.PanicLevel),
	)

	homeDir := path.Join(os.Getenv("HOME"), ".lens")
	keyDir := path.Join(homeDir, "keys")

	cosmosCCC := &lens.ChainClientConfig{
		Key:		"cosmos_acc",
		ChainID:	"cosmoshub-4",
		RPCAddr:	"https://cosmos-rpc.polkachu.com:443",
		AccountPrefix:	"cosmos",
		KeyringBackend:	"test",
		GasAdjustment:	1.2,
		GasPrices:	"0.025uatom",
		KeyDirectory:	keyDir,
		Debug:		false,
		Timeout:	"20s",
		OutputFormat:	"plain",
		SignModeStr:	"direct",
		Modules:	lens.ModuleBasics,
	}
	cosmosClient, err := lens.NewChainClient(logger, cosmosCCC, homeDir, os.Stdin, os.Stdout)
	if err != nil {
		log.Fatal(err)
	}

	osmoCCC := &lens.ChainClientConfig{
		Key:		"osmo_acc",
		ChainID:	"osmosis-1",
		RPCAddr:	"https://osmosis-rpc.polkachu.com:443",
		AccountPrefix:	"osmo",
		KeyringBackend:	"test",
		GasAdjustment:	1.2,
		GasPrices:	"0uosmo",
		KeyDirectory:	keyDir,
		Debug:		false,
		Timeout:	"20s",
		OutputFormat:	"plain",
		SignModeStr:	"direct",
		Modules:	lens.ModuleBasics,
	}
	osmoClient, err := lens.NewChainClient(logger, osmoCCC, homeDir, os.Stdin, os.Stdout)
	if err != nil {
		log.Fatal(err)
	}

	cosmosAddr, err := cosmosClient.GetKeyAddress()
	if err != nil {
		log.Fatal(err)
	}
	osmoAddr, err := osmoClient.GetKeyAddress()
	if err != nil {
		log.Fatal(err)
	}

	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		time.Sleep(time.Second)
		done := cosmosClient.SetSDKContext()
		fmt.Println("Cosmos: ", cosmosAddr.String())
		done()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		time.Sleep(time.Second)
		done := osmoClient.SetSDKContext()
		fmt.Println("Osmosis:", osmoAddr.String())
		done()
	}()

	wg.Wait()
}

This code example is intentionally racy as to show that the results are different each time you run the thing.

# Cosmos client runs first.
Cosmos:  cosmos13x77yexvf6qexfjg9czp6jhpv7vpjdwwnsrvej
Osmosis: cosmos13x77yexvf6qexfjg9czp6jhpv7vpjdwwnsrvej

# Osmosis client runs first.
Osmosis: osmo13x77yexvf6qexfjg9czp6jhpv7vpjdwwmtsu0q
Cosmos:  osmo13x77yexvf6qexfjg9czp6jhpv7vpjdwwmtsu0q
@KyleMoser
Copy link

Was just talking about this today with some folks and happened to see this issue.

This behavior is a known issue with the Cosmos SDK. When you call SetSDKContext, that setting is global within the SDK. I recommend that you avoid the AccAddress's .String() function and call EncodeBech32AccAddr in Lens client/address.go instead.

As a second option, you could call a function that holds the lock on the SDK context until its finished generating the address. For example, your function could be something like this:

sdkConfigMutex.Lock()
defer sdkConfigMutex.Unlock()
sdkConf := sdk.GetConfig()
sdkConf.SetBech32PrefixForAccount(cc.Config.AccountPrefix, cc.Config.AccountPrefix+"pub")
sdkConf.SetBech32PrefixForValidator(cc.Config.AccountPrefix+"valoper", cc.Config.AccountPrefix+"valoperpub")
sdkConf.SetBech32PrefixForConsensusNode(cc.Config.AccountPrefix+"valcons", cc.Config.AccountPrefix+"valconspub")
osmoAddr.String()

@Caruso33
Copy link
Contributor

Thanks @KyleMoser , the first solution seems to work 🙏🏼

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants