Skip to content

Commit

Permalink
Add tron integration
Browse files Browse the repository at this point in the history
  • Loading branch information
cfal committed Oct 17, 2024
1 parent 26e22eb commit ac8eace
Show file tree
Hide file tree
Showing 76 changed files with 3,380 additions and 49 deletions.
3 changes: 3 additions & 0 deletions core/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func NewApp(s *Shell) *cli.App {
keysCommand("Solana", NewSolanaKeysClient(s)),
keysCommand("StarkNet", NewStarkNetKeysClient(s)),
keysCommand("Aptos", NewAptosKeysClient(s)),
keysCommand("Tron", NewTronKeysClient(s)),

initVRFKeysSubCmd(s),
},
Expand Down Expand Up @@ -296,6 +297,7 @@ func NewApp(s *Shell) *cli.App {
chainCommand("Solana", SolanaChainClient(s),
cli.StringFlag{Name: "id", Usage: "chain ID, options: [mainnet, testnet, devnet, localnet]"}),
chainCommand("StarkNet", StarkNetChainClient(s), cli.StringFlag{Name: "id", Usage: "chain ID"}),
chainCommand("Tron", TronChainClient(s), cli.StringFlag{Name: "id", Usage: "chain ID"}),
},
},
{
Expand All @@ -306,6 +308,7 @@ func NewApp(s *Shell) *cli.App {
initCosmosNodeSubCmd(s),
initSolanaNodeSubCmd(s),
initStarkNetNodeSubCmd(s),
initTronNodeSubCmd(s),
},
},
{
Expand Down
4 changes: 4 additions & 0 deletions core/cmd/nodes_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func initSolanaNodeSubCmd(s *Shell) cli.Command {
return nodeCommand("Solana", NewSolanaNodeClient(s))
}

func initTronNodeSubCmd(s *Shell) cli.Command {
return nodeCommand("Tron", NewTronNodeClient(s))
}

// nodeCommand returns a cli.Command with subcommands for the given NodeClient.
// A string cli.Flag for "name" is automatically included.
func nodeCommand(typ string, client NodeClient) cli.Command {
Expand Down
7 changes: 7 additions & 0 deletions core/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G
}
initOps = append(initOps, chainlink.InitAptos(ctx, relayerFactory, aptosCfg))
}
if cfg.TronEnabled() {
tronCfg := chainlink.TronFactoryConfig{
Keystore: keyStore.Tron(),
TOMLConfigs: cfg.TronConfigs(),
}
initOps = append(initOps, chainlink.InitTron(ctx, relayerFactory, tronCfg))
}

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 @@ -438,6 +438,9 @@ func (s *Shell) runNode(c *cli.Context) error {
if s.Config.AptosEnabled() {
enabledChains = append(enabledChains, chaintype.Aptos)
}
if s.Config.TronEnabled() {
enabledChains = append(enabledChains, chaintype.Tron)
}
err2 := app.GetKeyStore().OCR2().EnsureKeys(rootCtx, enabledChains...)
if err2 != nil {
return errors.Wrap(err2, "failed to ensure ocr key")
Expand Down Expand Up @@ -473,6 +476,12 @@ func (s *Shell) runNode(c *cli.Context) error {
return errors.Wrap(err2, "failed to ensure aptos key")
}
}
if s.Config.TronEnabled() {
err2 := app.GetKeyStore().Tron().EnsureKey(rootCtx)
if err2 != nil {
return errors.Wrap(err2, "failed to ensure tron key")
}
}

err2 := app.GetKeyStore().CSA().EnsureKey(rootCtx)
if err2 != nil {
Expand Down
100 changes: 100 additions & 0 deletions core/cmd/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,106 @@ func TestSetupStarkNetRelayer(t *testing.T) {
require.Contains(t, err.Error(), "failed to create StarkNet LOOP command")
})
}
func TestSetupTronRelayer(t *testing.T) {
lggr := logger.TestLogger(t)
reg := plugins.NewLoopRegistry(lggr, nil, nil)
ks := mocks.NewTron(t)
// config 3 chains but only enable 2 => should only be 2 relayer
nEnabledChains := 2
tConfig := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.Tron = chainlink.RawConfigs{
chainlink.RawConfig{
"ChainID": "tron-id-1",
"Enabled": true,
},
chainlink.RawConfig{
"ChainID": "tron-id-2",
"Enabled": true,
},
chainlink.RawConfig{
"ChainID": "disabled-tron-id-1",
"Enabled": false,
},
}
})

t2Config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.Tron = chainlink.RawConfigs{
chainlink.RawConfig{
"ChainID": "tron-id-3",
"Enabled": true,
},
}
})
rf := chainlink.RelayerFactory{
Logger: lggr,
LoopRegistry: reg,
}

// not parallel; shared state
t.Run("no plugin", func(t *testing.T) {
relayers, err := rf.NewTron(ks, tConfig.TronConfigs())
require.NoError(t, err)
require.NotNil(t, relayers)
require.Len(t, relayers, nEnabledChains)
// no using plugin, so registry should be empty
require.Len(t, reg.List(), 0)
})

t.Run("plugin", func(t *testing.T) {
t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd")

relayers, err := rf.NewTron(ks, tConfig.TronConfigs())
require.NoError(t, err)
require.NotNil(t, relayers)
require.Len(t, relayers, nEnabledChains)
// make sure registry has the plugin
require.Len(t, reg.List(), nEnabledChains)
})

// test that duplicate enabled chains is an error when
duplicateConfig := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.Tron = chainlink.RawConfigs{
chainlink.RawConfig{
"ChainID": "dupe",
"Enabled": true,
},
chainlink.RawConfig{
"ChainID": "dupe",
"Enabled": true,
},
}
})

// not parallel; shared state
t.Run("no plugin, duplicate chains", func(t *testing.T) {
_, err := rf.NewTron(ks, duplicateConfig.TronConfigs())
require.Error(t, err)
})

t.Run("plugin, duplicate chains", func(t *testing.T) {
t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd")
_, err := rf.NewTron(ks, duplicateConfig.TronConfigs())
require.Error(t, err)
})

t.Run("plugin env parsing fails", func(t *testing.T) {
t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd")
t.Setenv("CL_STARKNET_ENV", "fake_path")

_, err := rf.NewTron(ks, t2Config.TronConfigs())
require.Error(t, err)
require.Contains(t, err.Error(), "failed to parse Starknet env file")
})

t.Run("plugin already registered", func(t *testing.T) {
t.Setenv("CL_STARKNET_CMD", "phony_starknet_cmd")

_, err := rf.NewTron(ks, tConfig.TronConfigs())
require.Error(t, err)
require.Contains(t, err.Error(), "failed to create StarkNet LOOP command")
})
}

// flagSetApplyFromAction applies the flags from action to the flagSet.
// `parentCommand` will filter the app commands and only applies the flags if the command/subcommand has a parent with that name, if left empty no filtering is done
Expand Down
48 changes: 48 additions & 0 deletions core/cmd/tron_chains_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"strconv"

"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)

// TronChainPresenter implements TableRenderer for a TronChainResource
type TronChainPresenter struct {
presenters.TronChainResource
}

// ToRow presents the TronChainResource as a slice of strings.
func (p *TronChainPresenter) ToRow() []string {
return []string{p.GetID(), strconv.FormatBool(p.Enabled), p.Config}
}

// RenderTable implements TableRenderer
// Just renders a single row
func (p TronChainPresenter) RenderTable(rt RendererTable) error {
rows := [][]string{}
rows = append(rows, p.ToRow())

renderList(chainHeaders, rows, rt.Writer)

return nil
}

// TronChainPresenters implements TableRenderer for a slice of TronChainPresenters.
type TronChainPresenters []TronChainPresenter

// RenderTable implements TableRenderer
func (ps TronChainPresenters) RenderTable(rt RendererTable) error {
rows := [][]string{}

for _, p := range ps {
rows = append(rows, p.ToRow())
}

renderList(chainHeaders, rows, rt.Writer)

return nil
}

func TronChainClient(s *Shell) ChainClient {
return newChainClient[TronChainPresenters](s, "tron")
}
57 changes: 57 additions & 0 deletions core/cmd/tron_keys_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package cmd

import (
"github.com/smartcontractkit/chainlink-common/pkg/utils"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)

type TronKeyPresenter struct {
JAID
presenters.TronKeyResource
}

// RenderTable implements TableRenderer
func (p TronKeyPresenter) RenderTable(rt RendererTable) error {
headers := []string{"ID", "Public key"}
rows := [][]string{p.ToRow()}

if _, err := rt.Write([]byte("🔑 Tron Keys\n")); err != nil {
return err
}
renderList(headers, rows, rt.Writer)

return utils.JustError(rt.Write([]byte("\n")))
}

func (p *TronKeyPresenter) ToRow() []string {
row := []string{
p.ID,
p.PubKey,
}

return row
}

type TronKeyPresenters []TronKeyPresenter

// RenderTable implements TableRenderer
func (ps TronKeyPresenters) RenderTable(rt RendererTable) error {
headers := []string{"ID", "Public key"}
rows := [][]string{}

for _, p := range ps {
rows = append(rows, p.ToRow())
}

if _, err := rt.Write([]byte("🔑 Tron Keys\n")); err != nil {
return err
}
renderList(headers, rows, rt.Writer)

return utils.JustError(rt.Write([]byte("\n")))
}

func NewTronKeysClient(s *Shell) KeysClient {
return newKeysClient[tronkey.Key, TronKeyPresenter, TronKeyPresenters]("Tron", s)
}
Loading

0 comments on commit ac8eace

Please sign in to comment.