Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func NewApp(s *Shell) *cli.App {
keysCommand("Solana", NewSolanaKeysClient(s)),
keysCommand("StarkNet", NewStarkNetKeysClient(s)),
keysCommand("Aptos", NewAptosKeysClient(s)),
keysCommand("Stellar", NewStellarKeysClient(s)),
keysCommand("Tron", NewTronKeysClient(s)),
keysCommand("TON", NewTONKeysClient(s)),
keysCommand("Sui", NewSuiKeysClient(s)),
Expand Down
58 changes: 58 additions & 0 deletions core/cmd/stellar_keys_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cmd

import (
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/stellarkey"
"github.com/smartcontractkit/chainlink-common/pkg/utils"

"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
Comment thread
ilija42 marked this conversation as resolved.
)

type StellarKeyPresenter struct {
JAID
presenters.StellarKeyResource
}

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

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

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

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

return row
}

type StellarKeyPresenters []StellarKeyPresenter

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

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

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

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

func NewStellarKeysClient(s *Shell) KeysClient {
return newKeysClient[stellarkey.Key, StellarKeyPresenter, StellarKeyPresenters]("Stellar", s)
}
179 changes: 179 additions & 0 deletions core/cmd/stellar_keys_commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
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/keystore/corekeys/stellarkey"
"github.com/smartcontractkit/chainlink-common/pkg/utils"

"github.com/smartcontractkit/chainlink/v2/core/cmd"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
Comment thread
ilija42 marked this conversation as resolved.
"github.com/smartcontractkit/chainlink/v2/core/services/chainlink"
"github.com/smartcontractkit/chainlink/v2/core/web/presenters"
)

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

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

p := cmd.StellarKeyPresenter{
JAID: cmd.JAID{ID: id},
StellarKeyResource: presenters.StellarKeyResource{
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.StellarKeyPresenters{p}
require.NoError(t, ps.RenderTable(r))

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

func TestShell_StellarKeys(t *testing.T) {
t.Parallel()
app := startNewApplicationV2(t, nil)
Comment thread
ilija42 marked this conversation as resolved.
ks := app.GetKeyStore().Stellar()
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())))
}
requireStellarKeyCount(t, app, 0)
}

t.Run("ListStellarKeys", func(tt *testing.T) {
t.Parallel()
defer cleanup()
ctx := testing.TB.Context(t)
Comment thread
ilija42 marked this conversation as resolved.
Outdated
client, r := app.NewShellAndRenderer()
key, err := app.GetKeyStore().Stellar().Create(ctx)
require.NoError(t, err)
requireStellarKeyCount(t, app, 1)
assert.NoError(t, cmd.NewStellarKeysClient(client).ListKeys(cltest.EmptyCLIContext()))
require.Len(t, r.Renders, 1)
keys := *r.Renders[0].(*cmd.StellarKeyPresenters)
assert.Equal(t, key.PublicKeyStr(), keys[0].PubKey)
})

t.Run("CreateStellarKey", func(tt *testing.T) {
t.Parallel()
defer cleanup()
Comment thread
ilija42 marked this conversation as resolved.
client, _ := app.NewShellAndRenderer()
Comment thread
ilija42 marked this conversation as resolved.
require.NoError(t, cmd.NewStellarKeysClient(client).CreateKey(nilContext))
keys, err := app.GetKeyStore().Stellar().GetAll()
require.NoError(t, err)
require.Len(t, keys, 1)
})

t.Run("DeleteStellarKey", func(tt *testing.T) {
t.Parallel()
defer cleanup()
ctx := testing.TB.Context(t)
Comment thread
ilija42 marked this conversation as resolved.
Outdated
client, _ := app.NewShellAndRenderer()
key, err := app.GetKeyStore().Stellar().Create(ctx)
require.NoError(t, err)
requireStellarKeyCount(t, app, 1)
set := flag.NewFlagSet("test", 0)
flagSetApplyFromAction(cmd.NewStellarKeysClient(client).DeleteKey, set, "stellar")

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.NewStellarKeysClient(client).DeleteKey(c)
require.NoError(t, err)
requireStellarKeyCount(t, app, 0)
})

t.Run("ImportExportStellarKey", func(tt *testing.T) {
t.Parallel()
defer cleanup()
defer deleteKeyExportFile(t)
Comment thread
ilija42 marked this conversation as resolved.
ctx := testing.TB.Context(t)
Comment thread
ilija42 marked this conversation as resolved.
Outdated
client, _ := app.NewShellAndRenderer()

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

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

// Export test invalid id
set := flag.NewFlagSet("test Stellar export", 0)
flagSetApplyFromAction(cmd.NewStellarKeysClient(client).ExportKey, set, "stellar")

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.NewStellarKeysClient(client).ExportKey(c)
require.Error(t, err, "Error exporting")
require.Error(t, utils.JustError(os.Stat(keyName)))

// Export test
set = flag.NewFlagSet("test Stellar export", 0)
flagSetApplyFromAction(cmd.NewStellarKeysClient(client).ExportKey, set, "stellar")

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.NewStellarKeysClient(client).ExportKey(c))
require.NoError(t, utils.JustError(os.Stat(keyName)))

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

set = flag.NewFlagSet("test Stellar import", 0)
flagSetApplyFromAction(cmd.NewStellarKeysClient(client).ImportKey, set, "stellar")

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.NewStellarKeysClient(client).ImportKey(c))

requireStellarKeyCount(t, app, 1)
})
}

func requireStellarKeyCount(t *testing.T, app chainlink.Application, length int) []stellarkey.Key {
t.Helper()
keys, err := app.GetKeyStore().Stellar().GetAll()
require.NoError(t, err)
require.Len(t, keys, length)
return keys
}
1 change: 1 addition & 0 deletions core/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var (
TronPlugin = NewPlugin("tron")
TONPlugin = NewPlugin("ton")
SuiPlugin = NewPlugin("sui")
StellarPlugin = NewPlugin("stellar")
CapabilitiesPlugin = NewPlugin("capabilities")
// PrometheusDiscoveryHostName is the externally accessible hostname
// published by the node in the `/discovery` endpoint. Generally, it is expected to match
Expand Down
4 changes: 4 additions & 0 deletions core/internal/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/workflows/metering"

commonkeystore "github.com/smartcontractkit/chainlink-common/keystore"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/stellarkey"
"github.com/smartcontractkit/chainlink-common/pkg/sqlutil"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime"
"github.com/smartcontractkit/chainlink-data-streams/llo/retirement"
"github.com/smartcontractkit/chainlink-framework/multinode"

"github.com/smartcontractkit/chainlink/v2/core/capabilities/compute"
Comment thread
ilija42 marked this conversation as resolved.

"github.com/smartcontractkit/chainlink-evm/pkg/assets"
Expand All @@ -72,6 +74,7 @@ import (
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/vrfkey"
"github.com/smartcontractkit/chainlink-data-streams/mercury/wsrpc"
"github.com/smartcontractkit/chainlink-data-streams/mercury/wsrpc/cache"

"github.com/smartcontractkit/chainlink/v2/core/bridges"
"github.com/smartcontractkit/chainlink/v2/core/capabilities"
remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types"
Expand Down Expand Up @@ -138,6 +141,7 @@ var (
DefaultTronKey = tronkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultTONKey = tonkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultSuiKey = suikey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultStellarKey = stellarkey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed))
DefaultVRFKey = vrfkey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed))
)

Expand Down
3 changes: 3 additions & 0 deletions core/services/chainlink/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,9 @@ func NewApplication(ctx context.Context, opts ApplicationOpts) (Application, err
if cfg.SuiEnabled() {
initOps = append(initOps, InitSui(relayerFactory, keyStore.Sui(), keyStore.CSA(), cfg.SuiConfigs()))
}
if cfg.StellarEnabled() {
initOps = append(initOps, InitStellar(relayerFactory, keyStore.Stellar(), keyStore.CSA(), cfg.StellarConfigs()))
}

relayChainInterops, err := NewCoreRelayerChainInteroperators(initOps...)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions core/services/chainlink/node_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ func (r nodePlatformSubmitterKeyReader) submitterKeysForRelay(ctx context.Contex
return nodePlatformKeyIDs(r.keyStore.TON())
case relay.NetworkSui:
return nodePlatformKeyIDs(r.keyStore.Sui())
case relay.NetworkStellar:
return nodePlatformKeyIDs(r.keyStore.Stellar())
default:
return nil, nil
}
Expand Down
18 changes: 18 additions & 0 deletions core/services/chainlink/relayer_chain_interoperators.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,24 @@ func InitSui(factory RelayerFactory, ks keystore.Sui, csaKS keystore.CSA, chainC
}
}

// InitStellar is an option for instantiating Stellar relayers.
func InitStellar(factory RelayerFactory, ks keystore.Stellar, csaKS keystore.CSA, chainCfgs RawConfigs) CoreRelayerChainInitFunc {
return func(op *CoreRelayerChainInteroperators) (err error) {
loopKs := &keystore.StellarLooppSigner{Stellar: ks}
relayers, err := factory.NewStellar(loopKs, &keystore.CSASigner{CSA: csaKS}, chainCfgs)
if err != nil {
return fmt.Errorf("failed to setup Stellar 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
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,16 @@
expectedChainCnt, expectedNodeCnt = tt.expectedStarknetChainCnt, tt.expectedStarknetNodeCnt
case relay.NetworkDummy:
expectedChainCnt, expectedNodeCnt = tt.expectedDummyChainCnt, tt.expectedDummyNodeCnt
case relay.NetworkAptos:
case relay.Net:
Comment thread
Copilot marked this conversation as resolved.
Outdated
t.Skip("aptos doesn't need a CoreRelayerChainInteroperator")
case relay.NetworkTron:
t.Skip("tron doesn't need a CoreRelayerChainInteroperator")
case relay.NetworkTON:
t.Skip("ton doesn't need a CoreRelayerChainInteroperator")
case relay.NetworkSui:
t.Skip("sui doesn't need a CoreRelayerChainInteroperator")

case relay.NetworkStellar:
t.Skip("stellar doesn't need a CoreRelayerChainInteroperator")
default:
require.Fail(t, "untested relay network", relayNetwork)
}
Expand Down
4 changes: 4 additions & 0 deletions core/services/chainlink/relayer_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ func (r *RelayerFactory) NewSui(ks coretypes.Keystore, ksCSA coretypes.Keystore,
return r.NewLOOPRelayer("Sui", relay.NetworkSui, env.SuiPlugin, ks, ksCSA, chainCfgs)
}

func (r *RelayerFactory) NewStellar(ks coretypes.Keystore, ksCSA coretypes.Keystore, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) {
return r.NewLOOPRelayer("Stellar", relay.NetworkStellar, env.StellarPlugin, ks, ksCSA, chainCfgs)
}

func (r *RelayerFactory) NewLOOPRelayer(name string, network string, plugin env.Plugin, ks, ksCSA coretypes.Keystore, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) {
relayers := make(map[types.RelayID]loop.Relayer)
lggr := logger.Named(r.Logger, name)
Expand Down
Loading