Skip to content
Open
Show file tree
Hide file tree
Changes from all 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"}
rows := make([][]string, 0, len(ps))

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)
}

//nolint:paralleltest // subtests share a single keystore/application instance
func TestShell_StellarKeys(t *testing.T) {
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)
}

//nolint:paralleltest // subtests share a single keystore/application instance
t.Run("ListStellarKeys", func(tt *testing.T) {
defer cleanup()
ctx := tt.Context()
Comment thread
ilija42 marked this conversation as resolved.
client, r := app.NewShellAndRenderer()
key, err := app.GetKeyStore().Stellar().Create(ctx)
require.NoError(t, err)
requireStellarKeyCount(t, app, 1)
require.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)
})

//nolint:paralleltest // subtests share a single keystore/application instance
t.Run("CreateStellarKey", func(tt *testing.T) {
defer cleanup()
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)
})

//nolint:paralleltest // subtests share a single keystore/application instance
t.Run("DeleteStellarKey", func(tt *testing.T) {
defer cleanup()
ctx := tt.Context()
Comment thread
ilija42 marked this conversation as resolved.
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)
})

//nolint:paralleltest // subtests share a single keystore/application instance
t.Run("ImportExportStellarKey", func(tt *testing.T) {
defer cleanup()
defer deleteKeyExportFile(t)
Comment thread
ilija42 marked this conversation as resolved.
ctx := tt.Context()
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 @@ -48,6 +48,7 @@ import (
"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"

"github.com/smartcontractkit/chainlink-evm/pkg/assets"
Expand All @@ -66,12 +67,14 @@ import (
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/solkey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/starkkey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/stellarkey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/suikey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/tonkey"
"github.com/smartcontractkit/chainlink-common/keystore/corekeys/tronkey"
"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 @@ -216,7 +216,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) {
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
Loading