Skip to content

Commit

Permalink
add TRON integration (#14783)
Browse files Browse the repository at this point in the history
* Add tron integration

* .changeset/wise-buttons-fry.md: add changeset

* .mockery.yaml: add tron keystore

* core/services: update mocks

* core: cleanup chain type

* add to graphql

* rename TronLoopKeystore -> TronLOOPKeystore

* clean up keystore addresses

* fix keystore chaintypes

* fix graphql errors

* fix linting

* fix linting

* chore: regenerate mock

---------

Co-authored-by: Calvin Wang <[email protected]>
Co-authored-by: Calvin <[email protected]>
Co-authored-by: Graham Goh <[email protected]>
  • Loading branch information
4 people authored Jan 9, 2025
1 parent 8270318 commit 0199523
Show file tree
Hide file tree
Showing 71 changed files with 2,723 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-buttons-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Add TRON integration #added
3 changes: 2 additions & 1 deletion .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ packages:
StarkNet:
config:
filename: starknet.go
Tron:
VRF:
Workflow:
github.com/smartcontractkit/chainlink/v2/core/services/ocr:
Expand Down Expand Up @@ -579,4 +580,4 @@ packages:
dir: "{{ .InterfaceDir }}"
github.com/smartcontractkit/chainlink/v2/core/capabilities/targets:
interfaces:
ContractValueGetter:
ContractValueGetter:
1 change: 1 addition & 0 deletions core/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,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
7 changes: 7 additions & 0 deletions core/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,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
6 changes: 6 additions & 0 deletions core/cmd/shell_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,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().Workflow().EnsureKey(rootCtx)
if err2 != nil {
Expand Down
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)
}
174 changes: 174 additions & 0 deletions core/cmd/tron_keys_commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package cmd_test

import (
"bytes"
"context"
"flag"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"

"github.com/smartcontractkit/chainlink-common/pkg/utils"
"github.com/smartcontractkit/chainlink/v2/core/cmd"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)

func TestTronKeyPresenter_RenderTable(t *testing.T) {
t.Parallel()

var (
id = "1"
pubKey = "somepubkey"
buffer = bytes.NewBufferString("")
r = cmd.RendererTable{Writer: buffer}
)

p := cmd.TronKeyPresenter{
JAID: cmd.JAID{ID: id},
TronKeyResource: presenters.TronKeyResource{
JAID: presenters.NewJAID(id),
PubKey: pubKey,
},
}

// Render a single resource
require.NoError(t, p.RenderTable(r))

output := buffer.String()
assert.Contains(t, output, id)
assert.Contains(t, output, pubKey)

// Render many resources
buffer.Reset()
ps := cmd.TronKeyPresenters{p}
require.NoError(t, ps.RenderTable(r))

output = buffer.String()
assert.Contains(t, output, id)
assert.Contains(t, output, pubKey)
}

func TestShell_TronKeys(t *testing.T) {
app := startNewApplicationV2(t, nil)
ks := app.GetKeyStore().Tron()
cleanup := func() {
ctx := context.Background()
keys, err := ks.GetAll()
require.NoError(t, err)
for _, key := range keys {
require.NoError(t, utils.JustError(ks.Delete(ctx, key.ID())))
}
requireTronKeyCount(t, app, 0)
}

t.Run("ListTronKeys", func(tt *testing.T) {
defer cleanup()
ctx := testutils.Context(t)
client, r := app.NewShellAndRenderer()
key, err := app.GetKeyStore().Tron().Create(ctx)
require.NoError(t, err)
requireTronKeyCount(t, app, 1)
require.NoError(t, cmd.NewTronKeysClient(client).ListKeys(cltest.EmptyCLIContext()))
require.Len(t, r.Renders, 1)
keys := *r.Renders[0].(*cmd.TronKeyPresenters)
assert.Equal(t, key.PublicKeyStr(), keys[0].PubKey)
})

t.Run("CreateTronKey", func(tt *testing.T) {
defer cleanup()
client, _ := app.NewShellAndRenderer()
require.NoError(t, cmd.NewTronKeysClient(client).CreateKey(nilContext))
keys, err := app.GetKeyStore().Tron().GetAll()
require.NoError(t, err)
require.Len(t, keys, 1)
})

t.Run("DeleteTronKey", func(tt *testing.T) {
defer cleanup()
ctx := testutils.Context(t)
client, _ := app.NewShellAndRenderer()
key, err := app.GetKeyStore().Tron().Create(ctx)
require.NoError(t, err)
requireTronKeyCount(t, app, 1)
set := flag.NewFlagSet("test", 0)
flagSetApplyFromAction(cmd.NewTronKeysClient(client).DeleteKey, set, "tron")

require.NoError(tt, set.Set("yes", "true"))

strID := key.ID()
err = set.Parse([]string{strID})
require.NoError(t, err)
c := cli.NewContext(nil, set, nil)
err = cmd.NewTronKeysClient(client).DeleteKey(c)
require.NoError(t, err)
requireTronKeyCount(t, app, 0)
})

t.Run("ImportExportTronKey", func(tt *testing.T) {
defer cleanup()
defer deleteKeyExportFile(t)
ctx := testutils.Context(t)
client, _ := app.NewShellAndRenderer()

_, err := app.GetKeyStore().Tron().Create(ctx)
require.NoError(t, err)

keys := requireTronKeyCount(t, app, 1)
key := keys[0]
keyName := keyNameForTest(t)

// Export test invalid id
set := flag.NewFlagSet("test Tron export", 0)
flagSetApplyFromAction(cmd.NewTronKeysClient(client).ExportKey, set, "tron")

require.NoError(tt, set.Parse([]string{"0"}))
require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt"))
require.NoError(tt, set.Set("output", keyName))

c := cli.NewContext(nil, set, nil)
err = cmd.NewTronKeysClient(client).ExportKey(c)
require.Error(t, err, "Error exporting")
require.Error(t, utils.JustError(os.Stat(keyName)))

// Export test
set = flag.NewFlagSet("test Tron export", 0)
flagSetApplyFromAction(cmd.NewTronKeysClient(client).ExportKey, set, "tron")

require.NoError(tt, set.Parse([]string{key.ID()}))
require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt"))
require.NoError(tt, set.Set("output", keyName))

c = cli.NewContext(nil, set, nil)

require.NoError(t, cmd.NewTronKeysClient(client).ExportKey(c))
require.NoError(t, utils.JustError(os.Stat(keyName)))

require.NoError(t, utils.JustError(app.GetKeyStore().Tron().Delete(ctx, key.ID())))
requireTronKeyCount(t, app, 0)

set = flag.NewFlagSet("test Tron import", 0)
flagSetApplyFromAction(cmd.NewTronKeysClient(client).ImportKey, set, "tron")

require.NoError(tt, set.Parse([]string{keyName}))
require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt"))
c = cli.NewContext(nil, set, nil)
require.NoError(t, cmd.NewTronKeysClient(client).ImportKey(c))

requireTronKeyCount(t, app, 1)
})
}

func requireTronKeyCount(t *testing.T, app chainlink.Application, length int) []tronkey.Key {
t.Helper()
keys, err := app.GetKeyStore().Tron().GetAll()
require.NoError(t, err)
require.Len(t, keys, length)
return keys
}
1 change: 1 addition & 0 deletions core/config/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type AppConfig interface {
SolanaEnabled() bool
StarkNetEnabled() bool
AptosEnabled() bool
TronEnabled() bool

Validate() error
ValidateDB() error
Expand Down
13 changes: 13 additions & 0 deletions core/config/docs/chains-tron.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[Tron]]
# ChainID is the Tron chain ID.
ChainID = 'foobar' # Example
# Enabled enables this chain.
Enabled = true # Default

[[Tron.Nodes]]
# Name is a unique (per-chain) identifier for this node.
Name = 'primary' # Example
# URL is the full node HTTP endpoint for this node.
URL = 'https://api.trongrid.io/wallet' # Example
# SolidityURL is the solidity node HTTP endpoint for this node.
SolidityURL = 'http://api.trongrid.io/wallet' # Example
1 change: 1 addition & 0 deletions core/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var (
MercuryPlugin = NewPlugin("mercury")
SolanaPlugin = NewPlugin("solana")
StarknetPlugin = NewPlugin("starknet")
TronPlugin = NewPlugin("tron")
// PrometheusDiscoveryHostName is the externally accessible hostname
// published by the node in the `/discovery` endpoint. Generally, it is expected to match
// the public hostname of node.
Expand Down
9 changes: 9 additions & 0 deletions core/internal/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/tronkey"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
"github.com/smartcontractkit/chainlink/v2/core/services/pipeline"
Expand Down Expand Up @@ -133,6 +134,7 @@ var (
DefaultSolanaKey = solkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultStarkNetKey = starkkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultAptosKey = aptoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultTronKey = tronkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultVRFKey = vrfkey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed))
)

Expand Down Expand Up @@ -471,6 +473,13 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn
}
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
7 changes: 7 additions & 0 deletions core/services/chainlink/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type Config struct {
Starknet stkcfg.TOMLConfigs `toml:",omitempty"`

Aptos RawConfigs `toml:",omitempty"`

Tron RawConfigs `toml:",omitempty"`
}

// RawConfigs is a list of RawConfig.
Expand Down Expand Up @@ -260,6 +262,7 @@ func (c *Config) TOMLString() (string, error) {
// warnings aggregates warnings from valueWarnings and deprecationWarnings
func (c *Config) warnings() (err error) {
deprecationErr := c.deprecationWarnings()

warningErr := c.valueWarnings()
err = multierr.Append(deprecationErr, warningErr)
_, list := commonconfig.MultiErrorList(err)
Expand Down Expand Up @@ -352,6 +355,10 @@ func (c *Config) SetFrom(f *Config) (err error) {
err = multierr.Append(err, commonconfig.NamedMultiErrorList(err5, "Aptos"))
}

if err6 := c.Tron.SetFrom(f.Tron); err6 != nil {
err = multierr.Append(err, commonconfig.NamedMultiErrorList(err6, "Tron"))
}

_, err = commonconfig.MultiErrorList(err)

return err
Expand Down
Loading

0 comments on commit 0199523

Please sign in to comment.