From 808531bfdd5c19e6dd516280fe2cb4bd48641eef Mon Sep 17 00:00:00 2001 From: Justin Tieri <37750742+jtieri@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:23:09 -0600 Subject: [PATCH] Add documentation comments to Penumbra-related code. This commit extends the Go documentation of the Penumbra-related types, state, and behaviors found across the entire codebase. A significant part of this update focuses on the structs and functions in Penumbra's chain logic and client node, as well as the related helpers and tests. These comments should make the purpose and functionality of the code more transparent and understandable for further development and usage. --- chain/cosmos/wasm/wasm.go | 2 +- chain/penumbra/penumbra_app_node.go | 106 +++++++++++++++++--- chain/penumbra/penumbra_chain.go | 33 ++++-- chain/penumbra/penumbra_client_node.go | 69 +++++++++++-- chain/penumbra/penumbra_client_node_test.go | 8 ++ chain/penumbra/penumbra_node.go | 8 +- chain/penumbra/wallet.go | 12 ++- contract/cosmwasm/compile.go | 14 +-- contract/cosmwasm/rust_optimizer.go | 19 ++-- contract/cosmwasm/workspace_optimizer.go | 20 ++-- relayer/hyperspace/hyperspace_commander.go | 2 +- 11 files changed, 225 insertions(+), 68 deletions(-) diff --git a/chain/cosmos/wasm/wasm.go b/chain/cosmos/wasm/wasm.go index abbfd7c52..46886314a 100644 --- a/chain/cosmos/wasm/wasm.go +++ b/chain/cosmos/wasm/wasm.go @@ -14,4 +14,4 @@ func WasmEncoding() *testutil.TestEncodingConfig { wasmtypes.RegisterInterfaces(cfg.InterfaceRegistry) return &cfg -} \ No newline at end of file +} diff --git a/chain/penumbra/penumbra_app_node.go b/chain/penumbra/penumbra_app_node.go index 96e3ade70..f85bfd3e5 100644 --- a/chain/penumbra/penumbra_app_node.go +++ b/chain/penumbra/penumbra_app_node.go @@ -18,6 +18,7 @@ import ( "go.uber.org/zap" ) +// PenumbraAppNode represents an instance of pcli. type PenumbraAppNode struct { log *zap.Logger @@ -36,6 +37,19 @@ type PenumbraAppNode struct { hostGRPCPort string } +// NewPenumbraAppNode creates a new instance of PenumbraAppNode with the provided parameters. +// It initializes the PenumbraAppNode struct, sets the logger, index, chain, Docker client, +// network ID, test name, and Docker image. +// It also creates a container lifecycle instance with the provided logger, Docker client, +// and node name. +// Next, it creates a Docker volume with labels for cleanup and owner identification. +// If volume creation fails, an error is returned. +// The volume name is set to the PenumbraAppNode volume name. +// The volume owner is set using the provided context, Docker client, volume name, image reference, +// test name, and UID/GID from the Docker image. +// If volume owner setting fails, an error is returned. +// Finally, the created PenumbraAppNode instance is returned along with a nil error, +// or a nil PenumbraAppNode and a non-nil error if any step in the process fails. func NewPenumbraAppNode( ctx context.Context, log *zap.Logger, @@ -60,6 +74,7 @@ func NewPenumbraAppNode( if err != nil { return nil, fmt.Errorf("creating penumbra volume: %w", err) } + pn.VolumeName = pv.Name if err := dockerutil.SetVolumeOwner(ctx, dockerutil.VolumeOwnerOptions{ Log: log, @@ -91,37 +106,43 @@ var exposedPorts = nat.PortSet{ nat.Port(metricsPort): {}, } -// Name of the test node container +// Name of the test node container. func (p *PenumbraAppNode) Name() string { return fmt.Sprintf("pd-%d-%s-%s", p.Index, p.Chain.Config().ChainID, p.TestName) } -// the hostname of the test node container +// HostName returns the hostname of the test node container. func (p *PenumbraAppNode) HostName() string { return dockerutil.CondenseHostName(p.Name()) } -// Bind returns the home folder bind point for running the node +// Bind returns the home folder bind point for running the node. func (p *PenumbraAppNode) Bind() []string { return []string{fmt.Sprintf("%s:%s", p.VolumeName, p.HomeDir())} } +// HomeDir returns the home directory location in the Docker filesystem. func (p *PenumbraAppNode) HomeDir() string { return "/home/heighliner" } +// CreateKey attempts to initialize a new pcli config file with a newly generated FullViewingKey and CustodyKey. func (p *PenumbraAppNode) CreateKey(ctx context.Context, keyName string) error { keyPath := filepath.Join(p.HomeDir(), "keys", keyName) pdUrl := fmt.Sprintf("http://%s:8080", p.HostName()) cmd := []string{"pcli", "--home", keyPath, "init", "--grpc-url", pdUrl, "soft-kms", "generate"} + _, stderr, err := p.Exec(ctx, cmd, nil) - // already exists error is okay + + // key already exists, error is okay if err != nil && !strings.Contains(string(stderr), "not empty;, refusing to initialize") { return err } + return nil } +// PcliConfig represents the config.toml file associated with an instance of pcli. type PcliConfig struct { GrpcURL string `toml:"grpc_url"` FullViewingKey string `toml:"full_viewing_key"` @@ -143,6 +164,7 @@ func (p *PenumbraAppNode) ReadFile(ctx context.Context, relPath string) ([]byte, return fileBz, nil } +// FullViewingKey attempts to read the FullViewingKey from the config.toml file associated with this instance of pcli. func (p *PenumbraAppNode) FullViewingKey(ctx context.Context, keyName string) (string, error) { keyPath := filepath.Join(p.HomeDir(), "keys", keyName) fileBz, err := p.ReadFile(ctx, keyPath+"config.toml") @@ -170,15 +192,15 @@ func (p *PenumbraAppNode) RecoverKey(ctx context.Context, keyName, mnemonic stri _, stderr, err := p.Exec(ctx, cmd, nil) - // already exists error is okay + // key already exists, error is okay if err != nil && !strings.Contains(string(stderr), "already exists, refusing to overwrite it") { return err } + return nil } -// initializes validator definition template file -// wallet must be generated first +// InitValidatorFile initializes validator definition template file, wallet must be generated first. func (p *PenumbraAppNode) InitValidatorFile(ctx context.Context, valKeyName string) error { keyPath := filepath.Join(p.HomeDir(), "keys", valKeyName) cmd := []string{ @@ -187,22 +209,31 @@ func (p *PenumbraAppNode) InitValidatorFile(ctx context.Context, valKeyName stri "validator", "definition", "template", "--file", p.ValidatorDefinitionTemplateFilePathContainer(), } + _, _, err := p.Exec(ctx, cmd, nil) return err } +// ValidatorDefinitionTemplateFilePathContainer returns the path to the validator.toml file associated with +// this instance of pcli. func (p *PenumbraAppNode) ValidatorDefinitionTemplateFilePathContainer() string { return filepath.Join(p.HomeDir(), "validator.toml") } +// ValidatorsInputFileContainer returns the path to the validators.json file associated with +// this instance of pcli. func (p *PenumbraAppNode) ValidatorsInputFileContainer() string { return filepath.Join(p.HomeDir(), "validators.json") } +// AllocationsInputFileContainer returns the path to the allocations.csv file that should be used +// to generate the genesis file before spinning up the network from a fresh genesis. func (p *PenumbraAppNode) AllocationsInputFileContainer() string { return filepath.Join(p.HomeDir(), "allocations.csv") } +// genesisFileContent attempts to read the contents of the genesis.json file associated with the +// network that we are attempting to initialize from genesis. func (p *PenumbraAppNode) genesisFileContent(ctx context.Context) ([]byte, error) { fr := dockerutil.NewFileRetriever(p.log, p.DockerClient, p.TestName) gen, err := fr.SingleFileContent(ctx, p.VolumeName, ".penumbra/testnet_data/node0/cometbft/config/genesis.json") @@ -213,6 +244,9 @@ func (p *PenumbraAppNode) genesisFileContent(ctx context.Context) ([]byte, error return gen, nil } +// GenerateGenesisFile attempts to create the validators.json file and the allocations.csv file, write the files to +// the Docker filesystem, and then generate the directory structure containing necessary files to create a +// new testnet from genesis via an instance of pd. func (p *PenumbraAppNode) GenerateGenesisFile( ctx context.Context, chainID string, @@ -255,9 +289,9 @@ func (p *PenumbraAppNode) GenerateGenesisFile( return err } +// GetAddress attempts to return a Penumbra address associated with a specified key name. func (p *PenumbraAppNode) GetAddress(ctx context.Context, keyName string) ([]byte, error) { keyPath := filepath.Join(p.HomeDir(), "keys", keyName) - //pdUrl := fmt.Sprintf("http://%s:8080", p.HostName()) cmd := []string{"pcli", "--home", keyPath, "view", "address"} stdout, _, err := p.Exec(ctx, cmd, nil) @@ -273,10 +307,10 @@ func (p *PenumbraAppNode) GetAddress(ctx context.Context, keyName string) ([]byt return []byte(addr), nil } +// GetBalance attempts to query the token balances for a specified key name via an instance of pcli. // TODO we need to change the func sig to take a denom then filter out the target denom bal from stdout func (p *PenumbraAppNode) GetBalance(ctx context.Context, keyName string) (int64, error) { keyPath := filepath.Join(p.HomeDir(), "keys", keyName) - //pdUrl := fmt.Sprintf("http://%s:8080", p.HostName()) cmd := []string{"pcli", "--home", keyPath, "view", "balance"} stdout, _, err := p.Exec(ctx, cmd, nil) @@ -288,26 +322,43 @@ func (p *PenumbraAppNode) GetBalance(ctx context.Context, keyName string) (int64 return 0, nil } +// GetAddressBech32m retrieves the address associated with the specified key name. +// It executes the 'pcli' command and parses the output to find the desired address. +// The 'pcli' command is executed with the '--home' flag and the home directory of the app node. +// If no address is found for the key name, it returns an error indicating that the address was not found. +// The function returns the retrieved address as a string and an error if any occurred. func (p *PenumbraAppNode) GetAddressBech32m(ctx context.Context, keyName string) (string, error) { cmd := []string{"pcli", "--home", p.HomeDir(), "addr", "list"} stdout, _, err := p.Exec(ctx, cmd, nil) if err != nil { return "", err } + addresses := strings.Split(string(stdout), "\n") for _, address := range addresses { fields := strings.Fields(address) if len(fields) < 3 { continue } + if fields[1] == keyName { return fields[2], nil } } - return "", errors.New("address not found") + return "", errors.New("address not found") } +// CreateNodeContainer creates a container for the PenumbraAppNode. +// It starts the PenumbraAppNode process with the specified tendermintAddress. +// The container is created using the CreateContainer method of the containerLifecycle object. +// The container is named using p.TestName. +// It is assigned the p.NetworkID network and runs on the p.Image image. +// The container also exposes the abciPort, grpcPort, and metricsPort. +// The container's home directory is set to p.HomeDir(). +// The command to start the PenumbraAppNode process is constructed using the pd command and its arguments. +// Additional environment variables can be set using the env parameter. +// The method returns any errors encountered during the container creation process. func (p *PenumbraAppNode) CreateNodeContainer(ctx context.Context, tendermintAddress string) error { cmd := []string{ "pd", "start", @@ -318,18 +369,24 @@ func (p *PenumbraAppNode) CreateNodeContainer(ctx context.Context, tendermintAdd "--home", p.HomeDir(), } - // TODO: remove after debugging - env := []string{ - "RUST_LOG=debug", - } + // env can be used to set environment variables to do things like set RUST_LOG=debug. + var env []string return p.containerLifecycle.CreateContainer(ctx, p.TestName, p.NetworkID, p.Image, exposedPorts, p.Bind(), p.HostName(), cmd, env) } +// StopContainer stops the running container for the PenumbraAppNode. func (p *PenumbraAppNode) StopContainer(ctx context.Context) error { return p.containerLifecycle.StopContainer(ctx) } +// StartContainer starts the test node container. +// It calls the StartContainer method of the containerLifecycle field to start the container. +// If an error occurs, it is returned. +// It then calls the GetHostPorts method of the containerLifecycle field to retrieve the host ports for RPC and gRPC. +// If an error occurs, it is returned. +// The obtained host ports are assigned to the hostRPCPort and hostGRPCPort fields of the PenumbraAppNode struct. +// Finally, nil is returned if everything is successful. func (p *PenumbraAppNode) StartContainer(ctx context.Context) error { if err := p.containerLifecycle.StartContainer(ctx); err != nil { return err @@ -345,7 +402,7 @@ func (p *PenumbraAppNode) StartContainer(ctx context.Context) error { return nil } -// Exec run a container for a specific job and block until the container exits +// Exec run a container for a specific job and blocks until the container exits. func (p *PenumbraAppNode) Exec(ctx context.Context, cmd []string, env []string) ([]byte, []byte, error) { job := dockerutil.NewImage(p.log, p.DockerClient, p.NetworkID, p.TestName, p.Image.Repository, p.Image.Version) opts := dockerutil.ContainerOptions{ @@ -353,10 +410,29 @@ func (p *PenumbraAppNode) Exec(ctx context.Context, cmd []string, env []string) Env: env, User: p.Image.UidGid, } + res := job.Run(ctx, cmd, opts) return res.Stdout, res.Stderr, res.Err } +// SendIBCTransfer sends an IBC transfer from the current node to a specified address and channel. +// +// Parameters: +// - ctx: The context of the method call. +// - channelID: The ID of the channel to send the transfer. +// - keyName: The name of the key used for signing the transaction. +// - amount: The amount to transfer (including address and denomination). +// - opts: Additional options for the transfer. +// +// Returns: +// - tx: The transaction information for the IBC transfer (partially filled). +// - error: An error if the transfer fails. +// +// Note: The `SendIBCTransfer` method uses the `pcli` command-line tool to execute a transaction +// +// to withdraw tokens from the specified channel and send them to the specified address. +// The `tx` object returned represents the transaction information for the IBC transfer. +// The function currently does not fill in all details of the `tx` object and requires further implementation. func (p *PenumbraAppNode) SendIBCTransfer(ctx context.Context, channelID, keyName string, amount ibc.WalletAmount, opts ibc.TransferOptions) (ibc.Tx, error) { keyPath := filepath.Join(p.HomeDir(), "keys", keyName) diff --git a/chain/penumbra/penumbra_chain.go b/chain/penumbra/penumbra_chain.go index 6067d76c1..e59c0692e 100644 --- a/chain/penumbra/penumbra_chain.go +++ b/chain/penumbra/penumbra_chain.go @@ -68,6 +68,7 @@ type PenumbraGenesisAppStateAllocation struct { Address string `json:"address"` } +// NewPenumbraChain returns a new instance of PenumbraChain. func NewPenumbraChain(log *zap.Logger, testName string, chainConfig ibc.ChainConfig, numValidators int, numFullNodes int) *PenumbraChain { registry := codectypes.NewInterfaceRegistry() cryptocodec.RegisterInterfaces(registry) @@ -84,40 +85,44 @@ func NewPenumbraChain(log *zap.Logger, testName string, chainConfig ibc.ChainCon } } +// Acknowledgements implements Chain interface. func (c *PenumbraChain) Acknowledgements(ctx context.Context, height uint64) ([]ibc.PacketAcknowledgement, error) { panic("implement me") } +// Timeouts implements Chain interface. func (c *PenumbraChain) Timeouts(ctx context.Context, height uint64) ([]ibc.PacketTimeout, error) { panic("implement me") } -// Implements Chain interface +// Config returns the Chain's ChainConfig. func (c *PenumbraChain) Config() ibc.ChainConfig { return c.cfg } -// Implements Chain interface +// Initialize creates the test node objects required for bootstrapping tests. func (c *PenumbraChain) Initialize(ctx context.Context, testName string, cli *client.Client, networkID string) error { return c.initializeChainNodes(ctx, testName, cli, networkID) } -// Exec implements chain interface. +// Exec attempts to execute an arbitrary cmd with specified env variables and returns the output returned to +// both stdout and stderr. func (c *PenumbraChain) Exec(ctx context.Context, cmd []string, env []string) (stdout, stderr []byte, err error) { return c.getFullNode().PenumbraAppNode.Exec(ctx, cmd, env) } +// getFullNode returns the first configured validator node in the network. func (c *PenumbraChain) getFullNode() *PenumbraNode { // use first validator return c.PenumbraNodes[0] } -// Implements Chain interface +// GetRPCAddress returns the RPC address associated with an underlying node's Tendermint host name. func (c *PenumbraChain) GetRPCAddress() string { return fmt.Sprintf("http://%s:26657", c.getFullNode().TendermintNode.HostName()) } -// Implements Chain interface +// GetGRPCAddress returns the GRPC address associated with an underlying node's Tendermint host name. func (c *PenumbraChain) GetGRPCAddress() string { return fmt.Sprintf("%s:9090", c.getFullNode().TendermintNode.HostName()) } @@ -134,6 +139,7 @@ func (c *PenumbraChain) GetHostGRPCAddress() string { return c.getFullNode().PenumbraAppNode.hostGRPCPort } +// HomeDir returns the PenumbraAppNode's home directory in the Docker filesystem. func (c *PenumbraChain) HomeDir() string { return c.getFullNode().PenumbraAppNode.HomeDir() } @@ -165,9 +171,8 @@ func (c *PenumbraChain) GetAddress(ctx context.Context, keyName string) ([]byte, return fn.PenumbraAppNode.GetAddress(ctx, keyName) } -// BuildWallet will return a Penumbra wallet -// If mnemonic != "", it will restore using that mnemonic -// If mnemonic == "", it will create a new key +// BuildWallet will return a Penumbra wallet. +// If mnemonic != "", it will restore using that mnemonic. If mnemonic == "", it will create a new key. func (c *PenumbraChain) BuildWallet(ctx context.Context, keyName string, mnemonic string) (ibc.Wallet, error) { if mnemonic != "" { if err := c.RecoverKey(ctx, keyName, mnemonic); err != nil { @@ -238,6 +243,7 @@ func (c *PenumbraChain) SendIBCTransfer( return fn.PenumbraClientNodes[keyName].SendIBCTransfer(ctx, channelID, amount, options) } +// ExportState implements Chain interface. func (c *PenumbraChain) ExportState(ctx context.Context, height int64) (string, error) { panic("implement me") } @@ -271,7 +277,7 @@ func (c *PenumbraChain) GetGasFeesInNativeDenom(gasPaid int64) int64 { return int64(fees) } -// creates the test node objects required for bootstrapping tests +// initializeChainNodes creates the test node objects required for bootstrapping tests. func (c *PenumbraChain) initializeChainNodes( ctx context.Context, testName string, @@ -298,6 +304,7 @@ func (c *PenumbraChain) initializeChainNodes( _ = rc.Close() } } + for i := 0; i < count; i++ { pn, err := NewPenumbraNode(ctx, i, c, cli, networkID, testName, chainCfg.Images[0], chainCfg.Images[1]) if err != nil { @@ -305,6 +312,7 @@ func (c *PenumbraChain) initializeChainNodes( } penumbraNodes = append(penumbraNodes, &pn) } + c.PenumbraNodes = penumbraNodes return nil @@ -332,6 +340,8 @@ type ValidatorWithIntPower struct { PubKeyBase64 string } +// Start sets up everything needed, (validators, gentx, fullnodes, peering, additional accounts), +// for the chain to start from genesis. func (c *PenumbraChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { validators := c.PenumbraNodes[:c.numValidators] fullnodes := c.PenumbraNodes[c.numValidators:] @@ -345,6 +355,7 @@ func (c *PenumbraChain) Start(testName string, ctx context.Context, additionalGe for i, v := range validators { v := v i := i + keyName := fmt.Sprintf("%s-%d", valKey, i) eg.Go(func() error { if err := v.TendermintNode.InitValidatorFiles(egCtx); err != nil { @@ -473,7 +484,7 @@ func (c *PenumbraChain) Start(testName string, ctx context.Context, additionalGe return c.start(ctx) } -// Bootstraps the chain and starts it from genesis +// start bootstraps the chain and starts it from genesis. func (c *PenumbraChain) start(ctx context.Context) error { // Copy the penumbra genesis to all tendermint nodes. genesisContent, err := c.PenumbraNodes[0].PenumbraAppNode.genesisFileContent(ctx) @@ -588,6 +599,8 @@ func (c *PenumbraChain) start(ctx context.Context) error { return eg.Wait() } +// CreateClientNode initializes a new instance of pclientd, with the FullViewingKey and CustodyKey, +// associated with the specified keyName. func (c *PenumbraChain) CreateClientNode( ctx context.Context, keyName string, diff --git a/chain/penumbra/penumbra_client_node.go b/chain/penumbra/penumbra_client_node.go index b5aa9a1ab..20a7830ad 100644 --- a/chain/penumbra/penumbra_client_node.go +++ b/chain/penumbra/penumbra_client_node.go @@ -17,7 +17,7 @@ import ( "github.com/docker/go-connections/nat" assetv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/asset/v1alpha1" ibcv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/component/ibc/v1alpha1" - shielded_poolv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/component/shielded_pool/v1alpha1" + shieldedpoolv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/component/shielded_pool/v1alpha1" keysv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/keys/v1alpha1" numv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/core/num/v1alpha1" custodyv1alpha1 "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra/custody/v1alpha1" @@ -30,6 +30,7 @@ import ( "google.golang.org/grpc/credentials/insecure" ) +// PenumbraClientNode represents an instance of pclientd. type PenumbraClientNode struct { log *zap.Logger @@ -51,6 +52,8 @@ type PenumbraClientNode struct { hostGRPCPort string } +// NewClientNode attempts to initialize a new instance of pclientd. +// It then attempts to create the Docker container lifecycle and the Docker volume before setting the volume owner. func NewClientNode( ctx context.Context, log *zap.Logger, @@ -88,6 +91,7 @@ func NewClientNode( if err != nil { return nil, fmt.Errorf("creating pclientd volume: %w", err) } + p.VolumeName = tv.Name if err := dockerutil.SetVolumeOwner(ctx, dockerutil.VolumeOwnerOptions{ Log: log, @@ -109,25 +113,35 @@ const ( pclientdPort = "8081/tcp" ) +// pclientdPorts is a variable of type nat.PortSet that represents the set of ports used by the pclientd service. +// Declaration: +// +// var pclientdPorts = nat.PortSet{ +// nat.Port(pclientdPort): {}, +// } +// +// Example usage: +// func (p *Penumbra var pclientdPorts = nat.PortSet{ nat.Port(pclientdPort): {}, } -// Name of the test node container +// Name of the test node container. func (p *PenumbraClientNode) Name() string { return fmt.Sprintf("pclientd-%d-%s-%s-%s", p.Index, p.KeyName, p.Chain.Config().ChainID, p.TestName) } -// the hostname of the test node container +// HostName returns the hostname of the test node container. func (p *PenumbraClientNode) HostName() string { return dockerutil.CondenseHostName(p.Name()) } -// Bind returns the home folder bind point for running the node +// Bind returns the home folder bind point for running the node. func (p *PenumbraClientNode) Bind() []string { return []string{fmt.Sprintf("%s:%s", p.VolumeName, p.HomeDir())} } +// HomeDir returns the home directory for this instance of pclientd in the Docker filesystem. func (p *PenumbraClientNode) HomeDir() string { return "/home/pclientd" } @@ -156,6 +170,13 @@ func (p *PenumbraClientNode) GetAddress(ctx context.Context) ([]byte, error) { return resp.Address.Inner, nil } +// SendFunds sends funds from the PenumbraClientNode to a specified address. +// It generates a transaction plan, gets authorization data for the transaction, +// builds and signs the transaction, and broadcasts it. +// Parameters: +// - ctx: The context of the operation. +// - amount: The amount of funds to send, including the address and denomination. +// Returns an error if any step of the process fails. func (p *PenumbraClientNode) SendFunds(ctx context.Context, amount ibc.WalletAmount) error { channel, err := grpc.Dial(p.hostGRPCPort, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { @@ -224,6 +245,10 @@ func (p *PenumbraClientNode) SendFunds(ctx context.Context, amount ibc.WalletAmo return nil } +// SendIBCTransfer sends an IBC transfer from the current PenumbraClientNode to a specified destination address on a specified channel. +// It dials a gRPC connection to the hostGRPCPort, and if successful, closes the connection before returning. It returns an error if dialing the gRPC connection fails. +// The function validates the address string on the current PenumbraClientNode instance. If the address string is empty, it returns an error. +// It translates the amount to a big integer and creates an `ibcv1alpha1.Ics20Withdrawal` with the amount, denom, destination address, return address, timeout height, timeout timestamp func (p *PenumbraClientNode) SendIBCTransfer( ctx context.Context, channelID string, @@ -239,7 +264,7 @@ func (p *PenumbraClientNode) SendIBCTransfer( } defer channel.Close() - // TODO may need to be more defensive than this. additionally we may want to validate the addr string + // TODO may need to be more defensive than this, additionally we may want to validate the addr string. if p.addrString == "" { return ibc.Tx{}, fmt.Errorf("address string was not cached on pclientd instance for key with name %s", p.KeyName) } @@ -330,6 +355,22 @@ func (p *PenumbraClientNode) SendIBCTransfer( }, nil } +// GetBalance retrieves the balance of a specific denom for the PenumbraClientNode. +// +// This method establishes a gRPC connection to the PenumbraClientNode using the hostGRPCPort information. +// It then creates a client for the ViewProtocolService and constructs a BalancesRequest with an AccountFilter and AssetIdFilter. +// A Balances stream response is obtained from the server. +// The balances are collected in a slice until the stream is done, or an error occurs. +// If no balances are found, an error is returned. +// Otherwise, the first balance in the slice is used to construct a math.Int value and returned. +// +// Parameters: +// - ctx: The context.Context used for the gRPC connection. +// - denom: The denomination of the asset to get the balance for. +// +// Returns: +// - math.Int: The balance of the specified denom. +// - error: An error if any occurred during the balance retrieval. func (p *PenumbraClientNode) GetBalance(ctx context.Context, denom string) (math.Int, error) { channel, err := grpc.Dial( p.hostGRPCPort, @@ -380,7 +421,9 @@ func (p *PenumbraClientNode) GetBalance(ctx context.Context, denom string) (math } // translateHiAndLo takes the high and low order bytes and decodes the two uint64 values into the single int128 value -// they represent. Since Go does not support native uint128 we make use of the big.Int type. +// they represent. +// +// Since Go does not support native uint128 we make use of the big.Int type. // see: https://github.com/penumbra-zone/penumbra/blob/4d175986f385e00638328c64d729091d45eb042a/crates/core/crypto/src/asset/amount.rs#L220-L240 func translateHiAndLo(hi, lo uint64) math.Int { hiBig := big.NewInt(0).SetUint64(hi) @@ -394,7 +437,7 @@ func translateHiAndLo(hi, lo uint64) math.Int { return math.NewIntFromBigInt(i) } -// translateBigInt converts a Cosmos SDK Int, which is a wrapper around Go's big.Int, into two uint64 values +// translateBigInt converts a Cosmos SDK Int, which is a wrapper around Go's big.Int, into two uint64 values. func translateBigInt(i math.Int) (uint64, uint64) { bz := i.BigInt().Bytes() @@ -430,8 +473,8 @@ func (p *PenumbraClientNode) GetDenomMetadata(ctx context.Context, assetId *asse } defer channel.Close() - queryClient := shielded_poolv1alpha1.NewQueryServiceClient(channel) - req := &shielded_poolv1alpha1.DenomMetadataByIdRequest{ + queryClient := shieldedpoolv1alpha1.NewQueryServiceClient(channel) + req := &shieldedpoolv1alpha1.DenomMetadataByIdRequest{ ChainId: p.Chain.Config().ChainID, AssetId: assetId, } @@ -471,6 +514,7 @@ func (p *PenumbraClientNode) Initialize(ctx context.Context, pdAddress, spendKey return p.WriteFile(ctx, buf.Bytes(), "config.toml") } +// CreateNodeContainer creates a container for the Penumbra client node. func (p *PenumbraClientNode) CreateNodeContainer(ctx context.Context) error { cmd := []string{ "pclientd", @@ -478,16 +522,18 @@ func (p *PenumbraClientNode) CreateNodeContainer(ctx context.Context) error { "start", } + // env can be used to set environment variables for this instance of pclientd to do things like set RUST_LOG=debug var env []string - env = append(env, "RUST_LOG=debug") return p.containerLifecycle.CreateContainer(ctx, p.TestName, p.NetworkID, p.Image, pclientdPorts, p.Bind(), p.HostName(), cmd, env) } +// StopContainer stops the container associated with the PenumbraClientNode. func (p *PenumbraClientNode) StopContainer(ctx context.Context) error { return p.containerLifecycle.StopContainer(ctx) } +// StartContainer starts the test node container. func (p *PenumbraClientNode) StartContainer(ctx context.Context) error { if err := p.containerLifecycle.StartContainer(ctx); err != nil { return err @@ -503,7 +549,7 @@ func (p *PenumbraClientNode) StartContainer(ctx context.Context) error { return nil } -// Exec run a container for a specific job and block until the container exits +// Exec runs a container for a specific job and blocks until the container exits. func (p *PenumbraClientNode) Exec(ctx context.Context, cmd []string, env []string) ([]byte, []byte, error) { job := dockerutil.NewImage(p.log, p.DockerClient, p.NetworkID, p.TestName, p.Image.Repository, p.Image.Version) opts := dockerutil.ContainerOptions{ @@ -511,6 +557,7 @@ func (p *PenumbraClientNode) Exec(ctx context.Context, cmd []string, env []strin Env: env, User: p.Image.UidGid, } + res := job.Run(ctx, cmd, opts) return res.Stdout, res.Stderr, res.Err } diff --git a/chain/penumbra/penumbra_client_node_test.go b/chain/penumbra/penumbra_client_node_test.go index 7111f2f92..b0fae7c2b 100644 --- a/chain/penumbra/penumbra_client_node_test.go +++ b/chain/penumbra/penumbra_client_node_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" ) +// TestBigIntDecoding tests the decoding of big integers. func TestBigIntDecoding(t *testing.T) { bigInt := math.NewInt(11235813) hi, lo := translateBigInt(bigInt) @@ -23,6 +24,13 @@ func TestBigIntDecoding(t *testing.T) { require.True(t, converted.Equal(bInt)) } +// TestIbcTransferTimeout tests the function ibcTransferTimeouts +// in order to verify that it behaves correctly under different scenarios. +// Scenario 1: both timeout values equal zero - return default timeout values +// Scenario 2: options has nil timeout value - return default timeout values +// Scenario 3: both timeout values equal non-zero values - use specified timeout values +// Scenario 4: only nanoseconds equals non-zero value - use specified value for timestamp and zero for height +// Scenario 5: only height equals non-zero value - use specified value for height and zero for timestamp func TestIbcTransferTimeout(t *testing.T) { defaultHeight, defaultTimestamp := defaultTransferTimeouts() zero := uint64(0) diff --git a/chain/penumbra/penumbra_node.go b/chain/penumbra/penumbra_node.go index 9e18cfdfc..1fdebf5be 100644 --- a/chain/penumbra/penumbra_node.go +++ b/chain/penumbra/penumbra_node.go @@ -11,6 +11,8 @@ import ( "go.uber.org/zap" ) +// PenumbraNode reporesents a node in the Penumbra network which consists of one instance of Tendermint, +// an instance of pcli, and zero or more instances of pclientd. type PenumbraNode struct { TendermintNode *tendermint.TendermintNode PenumbraAppNode *PenumbraAppNode @@ -19,10 +21,10 @@ type PenumbraNode struct { addrString string } +// PenumbraNodes is a slice of pointers that point to instances of PenumbraNode in memory. type PenumbraNodes []*PenumbraNode -// NewChainNode returns a penumbra chain node with tendermint and penumbra nodes -// with docker volumes created. +// NewPenumbraNode returns a penumbra chain node with tendermint and penumbra nodes, along with docker volumes created. func NewPenumbraNode( ctx context.Context, i int, @@ -51,6 +53,8 @@ func NewPenumbraNode( }, nil } +// CreateClientNode initializes a new instance of pclientd, with the specified FullViewingKey and CustodyKey, +// before attempting to create and start pclientd in a new Docker container. func (p *PenumbraNode) CreateClientNode( ctx context.Context, log *zap.Logger, diff --git a/chain/penumbra/wallet.go b/chain/penumbra/wallet.go index 0033bfb6c..41cbc88af 100644 --- a/chain/penumbra/wallet.go +++ b/chain/penumbra/wallet.go @@ -6,6 +6,7 @@ import ( var _ ibc.Wallet = &PenumbraWallet{} +// PenumbraWallet represents a wallet for the Penumbra application. type PenumbraWallet struct { mnemonic string address []byte @@ -13,6 +14,7 @@ type PenumbraWallet struct { chainCfg ibc.ChainConfig } +// NewWallet creates a new instance of PenumbraWallet with the provided parameters func NewWallet(keyname string, address []byte, mnemonic string, chainCfg ibc.ChainConfig) *PenumbraWallet { return &PenumbraWallet{ mnemonic: mnemonic, @@ -22,25 +24,29 @@ func NewWallet(keyname string, address []byte, mnemonic string, chainCfg ibc.Cha } } +// KeyName returns the key name associated with a PenumbraWallet instance. func (w *PenumbraWallet) KeyName() string { return w.keyName } -// Get Address formatted with chain's prefix +// FormattedAddress returns the formatted address associated with a PenumbraWallet instance. func (w *PenumbraWallet) FormattedAddress() string { return string(w.address) } -// Get mnemonic, only used for relayer wallets +// Mnemonic returns the mnemonic associated with a PenumbraWallet instance. func (w *PenumbraWallet) Mnemonic() string { return w.mnemonic } -// Get Address +// Address returns the slice of bytes representing this PenumbraWallet instance's address. func (w *PenumbraWallet) Address() []byte { return w.address } +// FormattedAddressWithPrefix returns the formatted address string with a given prefix. +// The prefix is a string that will be appended to the beginning of the address. +// It takes the address stored in the PenumbraWallet instance and converts it to a string. func (w *PenumbraWallet) FormattedAddressWithPrefix(prefix string) string { return string(w.address) } diff --git a/contract/cosmwasm/compile.go b/contract/cosmwasm/compile.go index e83e72e65..710ed50bb 100644 --- a/contract/cosmwasm/compile.go +++ b/contract/cosmwasm/compile.go @@ -2,11 +2,11 @@ package cosmwasm import ( "context" - "path/filepath" "fmt" - "runtime" "io" "os" + "path/filepath" + "runtime" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -71,17 +71,17 @@ func compile(image string, optVersion string, repoPath string) (string, error) { }, &container.HostConfig{ Mounts: []mount.Mount{ { - Type: mount.TypeBind, + Type: mount.TypeBind, Source: repoPathFull, Target: "/code", }, { - Type: mount.TypeVolume, - Source: filepath.Base(repoPathFull)+"_cache", + Type: mount.TypeVolume, + Source: filepath.Base(repoPathFull) + "_cache", Target: cacheDir, }, { - Type: mount.TypeVolume, + Type: mount.TypeVolume, Source: "registry_cache", Target: "/usr/local/cargo/registry", }, @@ -131,4 +131,4 @@ func compile(image string, optVersion string, repoPath string) (string, error) { } return repoPathFull, nil -} \ No newline at end of file +} diff --git a/contract/cosmwasm/rust_optimizer.go b/contract/cosmwasm/rust_optimizer.go index ae5e880ff..8cb6fc5ba 100644 --- a/contract/cosmwasm/rust_optimizer.go +++ b/contract/cosmwasm/rust_optimizer.go @@ -1,26 +1,26 @@ package cosmwasm import ( - "path/filepath" "fmt" "os" + "path/filepath" "strings" ) type Contract struct { - DockerImage string - Version string - RelativePath string + DockerImage string + Version string + RelativePath string wasmBinPathChan chan string - errChan chan error + errChan chan error } // NewContract return a contract struct, populated with defaults and its relative path // relativePath is the relative path to the contract on local machine func NewContract(relativePath string) *Contract { return &Contract{ - DockerImage: "cosmwasm/rust-optimizer", - Version: "0.14.0", + DockerImage: "cosmwasm/rust-optimizer", + Version: "0.14.0", RelativePath: relativePath, } } @@ -38,7 +38,8 @@ func (c *Contract) WithVersion(version string) *Contract { } // Compile will compile the contract -// cosmwasm/rust-optimizer is the expected docker image +// +// cosmwasm/rust-optimizer is the expected docker image func (c *Contract) Compile() *Contract { c.wasmBinPathChan = make(chan string) c.errChan = make(chan error, 1) @@ -83,4 +84,4 @@ func (c *Contract) WaitForCompile() (string, error) { case contractBinary = <-c.wasmBinPathChan: } return contractBinary, nil -} \ No newline at end of file +} diff --git a/contract/cosmwasm/workspace_optimizer.go b/contract/cosmwasm/workspace_optimizer.go index b42bfbb00..c23e573cf 100644 --- a/contract/cosmwasm/workspace_optimizer.go +++ b/contract/cosmwasm/workspace_optimizer.go @@ -2,26 +2,26 @@ package cosmwasm import ( "bufio" - "path/filepath" "fmt" "os" + "path/filepath" "strings" ) type Workspace struct { - DockerImage string - Version string - RelativePath string + DockerImage string + Version string + RelativePath string wasmBinariesChan chan map[string]string - errChan chan error + errChan chan error } // NewWorkspace returns a workspace struct, populated with defaults and its relative path // relativePath is the relative path to the workspace on local machine func NewWorkspace(relativePath string) *Workspace { return &Workspace{ - DockerImage: "cosmwasm/workspace-optimizer", - Version: "0.14.0", + DockerImage: "cosmwasm/workspace-optimizer", + Version: "0.14.0", RelativePath: relativePath, } } @@ -39,7 +39,9 @@ func (w *Workspace) WithVersion(version string) *Workspace { } // Compile will compile the workspace's contracts -// cosmwasm/workspace-optimizer is the expected docker image +// +// cosmwasm/workspace-optimizer is the expected docker image +// // The workspace object is returned, call WaitForCompile() to get results func (w *Workspace) Compile() *Workspace { w.wasmBinariesChan = make(chan map[string]string) @@ -95,4 +97,4 @@ func (w *Workspace) WaitForCompile() (map[string]string, error) { case contractBinaries = <-w.wasmBinariesChan: } return contractBinaries, nil -} \ No newline at end of file +} diff --git a/relayer/hyperspace/hyperspace_commander.go b/relayer/hyperspace/hyperspace_commander.go index e98544ddf..da42d12dc 100644 --- a/relayer/hyperspace/hyperspace_commander.go +++ b/relayer/hyperspace/hyperspace_commander.go @@ -30,7 +30,7 @@ type pathConfiguration struct { // pathChainConfig holds all values that will be required when interacting with a path. type pathChainConfig struct { - chainID string + chainID string } func (hyperspaceCommander) Name() string {