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

Aptos LOOP setup #14076

Merged
merged 12 commits into from
Aug 16, 2024
Merged
7 changes: 7 additions & 0 deletions core/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G
}
initOps = append(initOps, chainlink.InitStarknet(ctx, relayerFactory, starkCfg))
}
if cfg.AptosEnabled() {
aptosCfg := chainlink.AptosFactoryConfig{
Keystore: keyStore.Aptos(),
TOMLConfigs: cfg.AptosConfigs(),
}
initOps = append(initOps, chainlink.InitAptos(ctx, relayerFactory, aptosCfg))
}

relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions core/cmd/shell_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ func (s *Shell) runNode(c *cli.Context) error {
if s.Config.StarkNetEnabled() {
enabledChains = append(enabledChains, chaintype.StarkNet)
}
if s.Config.AptosEnabled() {
enabledChains = append(enabledChains, chaintype.Aptos)
}
err2 := app.GetKeyStore().OCR2().EnsureKeys(rootCtx, enabledChains...)
if err2 != nil {
return errors.Wrap(err2, "failed to ensure ocr key")
Expand Down Expand Up @@ -464,6 +467,12 @@ func (s *Shell) runNode(c *cli.Context) error {
return errors.Wrap(err2, "failed to ensure starknet key")
}
}
if s.Config.AptosEnabled() {
err2 := app.GetKeyStore().Aptos().EnsureKey(rootCtx)
if err2 != nil {
return errors.Wrap(err2, "failed to ensure aptos key")
}
}

err2 := app.GetKeyStore().CSA().EnsureKey(rootCtx)
if err2 != nil {
Expand Down
7 changes: 7 additions & 0 deletions core/internal/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,13 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn
}
initOps = append(initOps, chainlink.InitStarknet(testCtx, relayerFactory, starkCfg))
}
if cfg.AptosEnabled() {
aptosCfg := chainlink.AptosFactoryConfig{
Keystore: keyStore.Aptos(),
TOMLConfigs: cfg.AptosConfigs(),
}
initOps = append(initOps, chainlink.InitAptos(testCtx, relayerFactory, aptosCfg))
}

relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...)
if err != nil {
Expand Down
24 changes: 21 additions & 3 deletions core/services/chainlink/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@ type RawConfig map[string]any
// ValidateConfig returns an error if the Config is not valid for use, as-is.
func (c *RawConfig) ValidateConfig() (err error) {
if v, ok := (*c)["Enabled"]; ok {
if _, ok := v.(*bool); !ok {
err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Enabled", Value: v, Msg: "expected *bool"})
if _, ok := v.(bool); !ok {
err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Enabled", Value: v, Msg: "expected bool"})
}
}
if v, ok := (*c)["ChainID"]; ok {
if _, ok := v.(string); !ok {
err = multierr.Append(err, commonconfig.ErrInvalid{Name: "ChainID", Value: v, Msg: "expected string"})
}
}
return err
Expand All @@ -67,7 +72,17 @@ func (c *RawConfig) IsEnabled() bool {
return false
}

return (*c)["Enabled"] == nil || *(*c)["Enabled"].(*bool)
enabled, ok := (*c)["Enabled"].(bool)
return ok && enabled
}

func (c *RawConfig) ChainID() string {
if c == nil {
return ""
}

chainID, _ := (*c)["ChainID"].(string)
return chainID
}

// TOMLString returns a TOML encoded string.
Expand Down Expand Up @@ -170,6 +185,9 @@ func (c *Config) SetFrom(f *Config) (err error) {
err = multierr.Append(err, commonconfig.NamedMultiErrorList(err4, "Starknet"))
}

// the plugin should handle it's own defaults and merging
c.Aptos = f.Aptos
Comment on lines +188 to +189
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unfortunately not so simple, because we also support passing multiple files that override one another. I can't think of a simple short-term fix, but this will have to be solved another way eventually regardless 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

could you concat the files together, then only do the parse once?

Copy link
Contributor

Choose a reason for hiding this comment

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

Plain concat only works in special cases. We could write logic to crawl through the maps and set individual fields, but I'm not sure how easy that will be 🤔


_, err = commonconfig.MultiErrorList(err)

return err
Expand Down
4 changes: 4 additions & 0 deletions core/services/chainlink/config_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ func (g *generalConfig) StarknetConfigs() starknet.TOMLConfigs {
return g.c.Starknet
}

func (g *generalConfig) AptosConfigs() RawConfigs {
return g.c.Aptos
}

func (g *generalConfig) Validate() error {
return g.validate(g.secrets.Validate)
}
Expand Down
2 changes: 1 addition & 1 deletion core/services/chainlink/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,7 @@ func TestConfig_Validate(t *testing.T) {
- 1: 2 errors:
- ChainID: missing: required for all chains
- Nodes: missing: must have at least one node
- Aptos.0.Enabled: invalid value (1): expected *bool`},
- Aptos.0.Enabled: invalid value (1): expected bool`},
} {
t.Run(tt.name, func(t *testing.T) {
var c Config
Expand Down
49 changes: 49 additions & 0 deletions core/services/chainlink/mocks/general_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions core/services/chainlink/relayer_chain_interoperators.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,23 @@ func InitStarknet(ctx context.Context, factory RelayerFactory, config StarkNetFa
}
}

// InitAptos is a option for instantiating Aptos relayers
func InitAptos(ctx context.Context, factory RelayerFactory, config AptosFactoryConfig) CoreRelayerChainInitFunc {
return func(op *CoreRelayerChainInteroperators) (err error) {
relayers, err := factory.NewAptos(config.Keystore, config.TOMLConfigs)
if err != nil {
return fmt.Errorf("failed to setup aptos relayer: %w", err)
}

for id, relayer := range relayers {
op.srvs = append(op.srvs, relayer)
op.loopRelayers[id] = relayer
}

return nil
}
}

// Get a [loop.Relayer] by id
func (rs *CoreRelayerChainInteroperators) Get(id types.RelayID) (loop.Relayer, error) {
rs.mu.Lock()
Expand Down
70 changes: 66 additions & 4 deletions core/services/chainlink/relayer_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink"
starkchain "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/chain"
"github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config"
starkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config"
"github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
coreconfig "github.com/smartcontractkit/chainlink/v2/core/config"
"github.com/smartcontractkit/chainlink/v2/core/config/env"
Expand Down Expand Up @@ -171,12 +171,12 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solcfg.TOMLConf

type StarkNetFactoryConfig struct {
Keystore keystore.StarkNet
config.TOMLConfigs
starkcfg.TOMLConfigs
}

// TODO BCF-2606 consider consolidating the driving logic with that of NewSolana above via generics
// perhaps when we implement a Cosmos LOOP
func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOMLConfigs) (map[types.RelayID]loop.Relayer, error) {
func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs starkcfg.TOMLConfigs) (map[types.RelayID]loop.Relayer, error) {
starknetRelayers := make(map[types.RelayID]loop.Relayer)

var (
Expand Down Expand Up @@ -205,7 +205,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML
if cmdName := env.StarknetPlugin.Cmd.Get(); cmdName != "" {
// setup the starknet relayer to be a LOOP
cfgTOML, err := toml.Marshal(struct {
Starknet config.TOMLConfig
Starknet starkcfg.TOMLConfig
}{Starknet: *chainCfg})
if err != nil {
return nil, fmt.Errorf("failed to marshal StarkNet configs: %w", err)
Expand Down Expand Up @@ -301,3 +301,65 @@ func (r *RelayerFactory) NewCosmos(config CosmosFactoryConfig) (map[types.RelayI
}
return relayers, nil
}

type AptosFactoryConfig struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

[non-blocking discussion] We should also ideally get Beholder integrated and configured in the core Node itself, and then inject it here to make it available to the LOOP as a common core service. @mathewdgardner wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

Draft in progress here to follow beholder client PR: smartcontractkit/chainlink-common#696

Keystore keystore.Aptos
TOMLConfigs RawConfigs
}

func (r *RelayerFactory) NewAptos(ks keystore.Aptos, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: shouldn't we use the AptosFactoryConfig as input param here?

plugin := env.NewPlugin("aptos")
loopKs := &keystore.AptosLooppSigner{Aptos: ks}
return r.NewLOOPRelayer("Aptos", corerelay.NetworkAptos, plugin, loopKs, chainCfgs)
}

func (r *RelayerFactory) NewLOOPRelayer(name string, network string, plugin env.Plugin, ks coretypes.Keystore, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: consider grouping the keystore, config, and capRegistry in a type like PluginContext

relayers := make(map[types.RelayID]loop.Relayer)
lggr := r.Logger.Named(name)

unique := make(map[string]struct{})
// create one relayer per chain id
for _, chainCfg := range chainCfgs {
relayID := types.RelayID{Network: network, ChainID: chainCfg.ChainID()}
if _, alreadyExists := unique[relayID.Name()]; alreadyExists {
return nil, fmt.Errorf("duplicate chain definitions for %s", relayID.Name())
}
unique[relayID.Name()] = struct{}{}

// skip disabled chains from further processing
if !chainCfg.IsEnabled() {
lggr.Warnw("Skipping disabled chain", "id", relayID.ChainID)
continue
}

lggr2 := lggr.Named(relayID.ChainID)

cmdName := plugin.Cmd.Get()
if cmdName == "" {
return nil, fmt.Errorf("plugin not defined: %s", "")
}

// setup the relayer as a LOOP
cfgTOML, err := toml.Marshal(chainCfg)
if err != nil {
return nil, fmt.Errorf("failed to marshal configs: %w", err)
}

envVars, err := plugins.ParseEnvFile(plugin.Env.Get())
if err != nil {
return nil, fmt.Errorf("failed to parse env file: %w", err)
}
cmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{
ID: relayID.Name(),
Cmd: cmdName,
Env: envVars,
})
if err != nil {
return nil, fmt.Errorf("failed to create LOOP command: %w", err)
}
// the relayer service has a delicate keystore dependency. the value that is passed to NewRelayerService must
// be compatible with instantiating a starknet transaction manager KeystoreAdapter within the LOOPp executable.
relayers[relayID] = loop.NewRelayerService(lggr2, r.GRPCOpts, cmdFn, string(cfgTOML), ks, r.CapabilitiesRegistry)
}
return relayers, nil
}
1 change: 1 addition & 0 deletions core/services/chainlink/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type GeneralConfig interface {
CosmosConfigs() coscfg.TOMLConfigs
SolanaConfigs() solcfg.TOMLConfigs
StarknetConfigs() stkcfg.TOMLConfigs
AptosConfigs() RawConfigs
// ConfigTOML returns both the user provided and effective configuration as TOML.
ConfigTOML() (user, effective string)
}
72 changes: 0 additions & 72 deletions core/services/keystore/keys/aptoskey/account.go

This file was deleted.

Loading
Loading