Commit 38aa19f3 authored by Yann Hodique's avatar Yann Hodique Committed by GitHub

feat(kurtosis-devnet): expose jwt tokens in output (#13738)

* chore(kurtosis-devnet): split artifact management to its own package

* feat(kurtosis-devnet): add jwt tokens to output
parent 6c44b0b5
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/interfaces"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/jwt"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
) )
...@@ -15,7 +17,7 @@ func (a *enclaveSpecAdapter) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, error) ...@@ -15,7 +17,7 @@ func (a *enclaveSpecAdapter) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, error)
return spec.NewSpec().ExtractData(r) return spec.NewSpec().ExtractData(r)
} }
var _ EnclaveSpecifier = (*enclaveSpecAdapter)(nil) var _ interfaces.EnclaveSpecifier = (*enclaveSpecAdapter)(nil)
type enclaveInspectAdapter struct{} type enclaveInspectAdapter struct{}
...@@ -23,7 +25,7 @@ func (a *enclaveInspectAdapter) EnclaveInspect(ctx context.Context, enclave stri ...@@ -23,7 +25,7 @@ func (a *enclaveInspectAdapter) EnclaveInspect(ctx context.Context, enclave stri
return inspect.NewInspector(enclave).ExtractData(ctx) return inspect.NewInspector(enclave).ExtractData(ctx)
} }
var _ EnclaveInspecter = (*enclaveInspectAdapter)(nil) var _ interfaces.EnclaveInspecter = (*enclaveInspectAdapter)(nil)
type enclaveDeployerAdapter struct{} type enclaveDeployerAdapter struct{}
...@@ -31,4 +33,12 @@ func (a *enclaveDeployerAdapter) EnclaveObserve(ctx context.Context, enclave str ...@@ -31,4 +33,12 @@ func (a *enclaveDeployerAdapter) EnclaveObserve(ctx context.Context, enclave str
return deployer.NewDeployer(enclave).ExtractData(ctx) return deployer.NewDeployer(enclave).ExtractData(ctx)
} }
var _ EnclaveObserver = (*enclaveDeployerAdapter)(nil) var _ interfaces.EnclaveObserver = (*enclaveDeployerAdapter)(nil)
type enclaveJWTAdapter struct{}
func (a *enclaveJWTAdapter) ExtractData(ctx context.Context, enclave string) (*jwt.Data, error) {
return jwt.NewExtractor(enclave).ExtractData(ctx)
}
var _ interfaces.JWTExtractor = (*enclaveJWTAdapter)(nil)
...@@ -6,11 +6,12 @@ import ( ...@@ -6,11 +6,12 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces" apiInterfaces "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/run" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/run"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/wrappers" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/wrappers"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
srcInterfaces "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/interfaces"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
) )
...@@ -39,6 +40,7 @@ type Chain struct { ...@@ -39,6 +40,7 @@ type Chain struct {
Nodes []Node `json:"nodes"` Nodes []Node `json:"nodes"`
Addresses deployer.DeploymentAddresses `json:"addresses,omitempty"` Addresses deployer.DeploymentAddresses `json:"addresses,omitempty"`
Wallets WalletMap `json:"wallets,omitempty"` Wallets WalletMap `json:"wallets,omitempty"`
JWT string `json:"jwt,omitempty"`
} }
type Wallet struct { type Wallet struct {
...@@ -65,10 +67,14 @@ type KurtosisDeployer struct { ...@@ -65,10 +67,14 @@ type KurtosisDeployer struct {
// Enclave name // Enclave name
enclave string enclave string
enclaveSpec EnclaveSpecifier // interfaces for kurtosis sources
enclaveInspecter EnclaveInspecter enclaveSpec srcInterfaces.EnclaveSpecifier
enclaveObserver EnclaveObserver enclaveInspecter srcInterfaces.EnclaveInspecter
kurtosisCtx interfaces.KurtosisContextInterface enclaveObserver srcInterfaces.EnclaveObserver
jwtExtractor srcInterfaces.JWTExtractor
// interface for kurtosis interactions
kurtosisCtx apiInterfaces.KurtosisContextInterface
} }
type KurtosisDeployerOptions func(*KurtosisDeployer) type KurtosisDeployerOptions func(*KurtosisDeployer)
...@@ -97,25 +103,31 @@ func WithKurtosisEnclave(enclave string) KurtosisDeployerOptions { ...@@ -97,25 +103,31 @@ func WithKurtosisEnclave(enclave string) KurtosisDeployerOptions {
} }
} }
func WithKurtosisEnclaveSpec(enclaveSpec EnclaveSpecifier) KurtosisDeployerOptions { func WithKurtosisEnclaveSpec(enclaveSpec srcInterfaces.EnclaveSpecifier) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) { return func(d *KurtosisDeployer) {
d.enclaveSpec = enclaveSpec d.enclaveSpec = enclaveSpec
} }
} }
func WithKurtosisEnclaveInspecter(enclaveInspecter EnclaveInspecter) KurtosisDeployerOptions { func WithKurtosisEnclaveInspecter(enclaveInspecter srcInterfaces.EnclaveInspecter) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) { return func(d *KurtosisDeployer) {
d.enclaveInspecter = enclaveInspecter d.enclaveInspecter = enclaveInspecter
} }
} }
func WithKurtosisEnclaveObserver(enclaveObserver EnclaveObserver) KurtosisDeployerOptions { func WithKurtosisEnclaveObserver(enclaveObserver srcInterfaces.EnclaveObserver) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) { return func(d *KurtosisDeployer) {
d.enclaveObserver = enclaveObserver d.enclaveObserver = enclaveObserver
} }
} }
func WithKurtosisKurtosisContext(kurtosisCtx interfaces.KurtosisContextInterface) KurtosisDeployerOptions { func WithKurtosisJWTExtractor(extractor srcInterfaces.JWTExtractor) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.jwtExtractor = extractor
}
}
func WithKurtosisKurtosisContext(kurtosisCtx apiInterfaces.KurtosisContextInterface) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) { return func(d *KurtosisDeployer) {
d.kurtosisCtx = kurtosisCtx d.kurtosisCtx = kurtosisCtx
} }
...@@ -132,6 +144,7 @@ func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) (*KurtosisDeployer, er ...@@ -132,6 +144,7 @@ func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) (*KurtosisDeployer, er
enclaveSpec: &enclaveSpecAdapter{}, enclaveSpec: &enclaveSpecAdapter{},
enclaveInspecter: &enclaveInspectAdapter{}, enclaveInspecter: &enclaveInspectAdapter{},
enclaveObserver: &enclaveDeployerAdapter{}, enclaveObserver: &enclaveDeployerAdapter{},
jwtExtractor: &enclaveJWTAdapter{},
} }
for _, opt := range opts { for _, opt := range opts {
...@@ -173,6 +186,12 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -173,6 +186,12 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En
return nil, fmt.Errorf("failed to parse deployer state: %w", err) return nil, fmt.Errorf("failed to parse deployer state: %w", err)
} }
// Get JWT data
jwtData, err := d.jwtExtractor.ExtractData(ctx, d.enclave)
if err != nil {
return nil, fmt.Errorf("failed to extract JWT data: %w", err)
}
env := &KurtosisEnvironment{ env := &KurtosisEnvironment{
L2: make([]*Chain, 0, len(spec.Chains)), L2: make([]*Chain, 0, len(spec.Chains)),
} }
...@@ -184,6 +203,7 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -184,6 +203,7 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En
Name: "Ethereum", Name: "Ethereum",
Services: services, Services: services,
Nodes: nodes, Nodes: nodes,
JWT: jwtData.L1JWT,
} }
if deployerState.State != nil { if deployerState.State != nil {
chain.Addresses = deployerState.State.Addresses chain.Addresses = deployerState.State.Addresses
...@@ -201,6 +221,7 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -201,6 +221,7 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En
ID: chainSpec.NetworkID, ID: chainSpec.NetworkID,
Services: services, Services: services,
Nodes: nodes, Nodes: nodes,
JWT: jwtData.L2JWT,
} }
// Add contract addresses if available // Add contract addresses if available
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/jwt"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -104,6 +105,16 @@ func (f *fakeEnclaveSpecifier) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, erro ...@@ -104,6 +105,16 @@ func (f *fakeEnclaveSpecifier) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, erro
return f.spec, f.err return f.spec, f.err
} }
// fakeJWTExtractor implements interfaces.JWTExtractor for testing
type fakeJWTExtractor struct {
data *jwt.Data
err error
}
func (f *fakeJWTExtractor) ExtractData(ctx context.Context, enclave string) (*jwt.Data, error) {
return f.data, f.err
}
func TestDeploy(t *testing.T) { func TestDeploy(t *testing.T) {
testSpec := &spec.EnclaveSpec{ testSpec := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{ Chains: []spec.ChainSpec{
...@@ -206,3 +217,131 @@ func TestDeploy(t *testing.T) { ...@@ -206,3 +217,131 @@ func TestDeploy(t *testing.T) {
}) })
} }
} }
func TestGetEnvironmentInfo(t *testing.T) {
testSpec := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{
{
Name: "op-kurtosis",
NetworkID: "1234",
},
},
}
// Create test services map with the expected structure
testServices := make(inspect.ServiceMap)
testServices["el-1-geth-lighthouse"] = inspect.PortMap{
"rpc": {Port: 52645},
}
testWallets := deployer.WalletList{
{
Name: "test-wallet",
Address: "0x123",
PrivateKey: "0xabc",
},
}
testJWTs := &jwt.Data{
L1JWT: "test-l1-jwt",
L2JWT: "test-l2-jwt",
}
// Create expected L1 services
l1Services := make(ServiceMap)
l1Services["el"] = Service{
Name: "el-1-geth-lighthouse",
Endpoints: EndpointMap{
"rpc": inspect.PortInfo{Port: 52645},
},
}
tests := []struct {
name string
spec *spec.EnclaveSpec
inspect *inspect.InspectData
deploy *deployer.DeployerData
jwt *jwt.Data
want *KurtosisEnvironment
wantErr bool
err error
}{
{
name: "successful environment info with JWT",
spec: testSpec,
inspect: &inspect.InspectData{UserServices: testServices},
deploy: &deployer.DeployerData{Wallets: testWallets},
jwt: testJWTs,
want: &KurtosisEnvironment{
L1: &Chain{
Name: "Ethereum",
Services: make(ServiceMap),
Nodes: []Node{
{
Services: l1Services,
},
},
JWT: testJWTs.L1JWT,
},
L2: []*Chain{
{
Name: "op-kurtosis",
ID: "1234",
Services: make(ServiceMap),
JWT: testJWTs.L2JWT,
},
},
},
},
{
name: "inspect error",
spec: testSpec,
err: fmt.Errorf("inspect failed"),
wantErr: true,
},
{
name: "deploy error",
spec: testSpec,
inspect: &inspect.InspectData{UserServices: testServices},
err: fmt.Errorf("deploy failed"),
wantErr: true,
},
{
name: "jwt error",
spec: testSpec,
inspect: &inspect.InspectData{UserServices: testServices},
deploy: &deployer.DeployerData{},
err: fmt.Errorf("jwt failed"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
deployer, err := NewKurtosisDeployer(
WithKurtosisKurtosisContext(&fake.KurtosisContext{}),
WithKurtosisEnclaveInspecter(&fakeEnclaveInspecter{
result: tt.inspect,
err: tt.err,
}),
WithKurtosisEnclaveObserver(&fakeEnclaveObserver{
state: tt.deploy,
err: tt.err,
}),
WithKurtosisJWTExtractor(&fakeJWTExtractor{
data: tt.jwt,
err: tt.err,
}),
)
require.NoError(t, err)
got, err := deployer.GetEnvironmentInfo(context.Background(), tt.spec)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, got)
})
}
}
package deployer package artifact
import ( import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context" "context"
"fmt"
"io" "io"
"path/filepath" "path/filepath"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/enclaves"
"github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context" "github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context"
) )
// EnclaveContextIface abstracts the EnclaveContext for testing
type EnclaveContextIface interface {
DownloadFilesArtifact(ctx context.Context, name string) ([]byte, error)
}
type EnclaveFS struct { type EnclaveFS struct {
enclaveCtx *enclaves.EnclaveContext enclaveCtx EnclaveContextIface
} }
func NewEnclaveFS(ctx context.Context, enclave string) (*EnclaveFS, error) { func NewEnclaveFS(ctx context.Context, enclave string) (*EnclaveFS, error) {
...@@ -31,6 +34,11 @@ func NewEnclaveFS(ctx context.Context, enclave string) (*EnclaveFS, error) { ...@@ -31,6 +34,11 @@ func NewEnclaveFS(ctx context.Context, enclave string) (*EnclaveFS, error) {
return &EnclaveFS{enclaveCtx: enclaveCtx}, nil return &EnclaveFS{enclaveCtx: enclaveCtx}, nil
} }
// NewEnclaveFSWithContext creates an EnclaveFS with a provided context (useful for testing)
func NewEnclaveFSWithContext(ctx EnclaveContextIface) *EnclaveFS {
return &EnclaveFS{enclaveCtx: ctx}
}
type Artifact struct { type Artifact struct {
reader *tar.Reader reader *tar.Reader
} }
...@@ -55,11 +63,17 @@ type ArtifactFileWriter struct { ...@@ -55,11 +63,17 @@ type ArtifactFileWriter struct {
writer io.Writer writer io.Writer
} }
func NewArtifactFileWriter(path string, writer io.Writer) *ArtifactFileWriter {
return &ArtifactFileWriter{
path: path,
writer: writer,
}
}
func (a *Artifact) ExtractFiles(writers ...*ArtifactFileWriter) error { func (a *Artifact) ExtractFiles(writers ...*ArtifactFileWriter) error {
paths := make(map[string]io.Writer) paths := make(map[string]io.Writer)
for _, writer := range writers { for _, writer := range writers {
canonicalPath := filepath.Clean(writer.path) canonicalPath := filepath.Clean(writer.path)
canonicalPath = fmt.Sprintf("./%s", canonicalPath)
paths[canonicalPath] = writer.writer paths[canonicalPath] = writer.writer
} }
...@@ -69,11 +83,12 @@ func (a *Artifact) ExtractFiles(writers ...*ArtifactFileWriter) error { ...@@ -69,11 +83,12 @@ func (a *Artifact) ExtractFiles(writers ...*ArtifactFileWriter) error {
break break
} }
if _, ok := paths[header.Name]; !ok { headerPath := filepath.Clean(header.Name)
if _, ok := paths[headerPath]; !ok {
continue continue
} }
writer := paths[header.Name] writer := paths[headerPath]
_, err = io.Copy(writer, a.reader) _, err = io.Copy(writer, a.reader)
if err != nil { if err != nil {
return err return err
......
package artifact
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"testing"
"github.com/stretchr/testify/require"
)
type mockEnclaveContext struct {
artifacts map[string][]byte
}
func (m *mockEnclaveContext) DownloadFilesArtifact(_ context.Context, name string) ([]byte, error) {
return m.artifacts[name], nil
}
func createTarGzArtifact(t *testing.T, files map[string]string) []byte {
var buf bytes.Buffer
gzWriter := gzip.NewWriter(&buf)
tarWriter := tar.NewWriter(gzWriter)
for name, content := range files {
err := tarWriter.WriteHeader(&tar.Header{
Name: name,
Mode: 0600,
Size: int64(len(content)),
})
require.NoError(t, err)
_, err = tarWriter.Write([]byte(content))
require.NoError(t, err)
}
require.NoError(t, tarWriter.Close())
require.NoError(t, gzWriter.Close())
return buf.Bytes()
}
func TestArtifactExtraction(t *testing.T) {
tests := []struct {
name string
files map[string]string
requests map[string]string
wantErr bool
}{
{
name: "simple path",
files: map[string]string{
"file1.txt": "content1",
},
requests: map[string]string{
"file1.txt": "content1",
},
},
{
name: "path with dot prefix",
files: map[string]string{
"./file1.txt": "content1",
},
requests: map[string]string{
"file1.txt": "content1",
},
},
{
name: "mixed paths",
files: map[string]string{
"./file1.txt": "content1",
"file2.txt": "content2",
"./dir/f3.txt": "content3",
},
requests: map[string]string{
"file1.txt": "content1",
"file2.txt": "content2",
"dir/f3.txt": "content3",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create mock context with artifact
mockCtx := &mockEnclaveContext{
artifacts: map[string][]byte{
"test-artifact": createTarGzArtifact(t, tt.files),
},
}
fs := NewEnclaveFSWithContext(mockCtx)
artifact, err := fs.GetArtifact(context.Background(), "test-artifact")
require.NoError(t, err)
// Create writers for all requested files
writers := make([]*ArtifactFileWriter, 0, len(tt.requests))
buffers := make(map[string]*bytes.Buffer, len(tt.requests))
for reqPath := range tt.requests {
buf := &bytes.Buffer{}
buffers[reqPath] = buf
writers = append(writers, NewArtifactFileWriter(reqPath, buf))
}
// Extract all files at once
err = artifact.ExtractFiles(writers...)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
// Verify contents
for reqPath, wantContent := range tt.requests {
require.Equal(t, wantContent, buffers[reqPath].String(), "content mismatch for %s", reqPath)
}
})
}
}
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"io" "io"
"math/big" "math/big"
"strings" "strings"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/artifact"
) )
const ( const (
...@@ -242,21 +244,21 @@ func parseStateFile(r io.Reader) (*DeployerState, error) { ...@@ -242,21 +244,21 @@ func parseStateFile(r io.Reader) (*DeployerState, error) {
// ExtractData downloads and parses the op-deployer state // ExtractData downloads and parses the op-deployer state
func (d *Deployer) ExtractData(ctx context.Context) (*DeployerData, error) { func (d *Deployer) ExtractData(ctx context.Context) (*DeployerData, error) {
fs, err := NewEnclaveFS(ctx, d.enclave) fs, err := artifact.NewEnclaveFS(ctx, d.enclave)
if err != nil { if err != nil {
return nil, err return nil, err
} }
artifact, err := fs.GetArtifact(ctx, d.deployerArtifactName) a, err := fs.GetArtifact(ctx, d.deployerArtifactName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
stateBuffer := bytes.NewBuffer(nil) stateBuffer := bytes.NewBuffer(nil)
walletsBuffer := bytes.NewBuffer(nil) walletsBuffer := bytes.NewBuffer(nil)
if err := artifact.ExtractFiles( if err := a.ExtractFiles(
&ArtifactFileWriter{path: d.stateName, writer: stateBuffer}, artifact.NewArtifactFileWriter(d.stateName, stateBuffer),
&ArtifactFileWriter{path: d.walletsName, writer: walletsBuffer}, artifact.NewArtifactFileWriter(d.walletsName, walletsBuffer),
); err != nil { ); err != nil {
return nil, err return nil, err
} }
...@@ -283,5 +285,8 @@ func (d *Deployer) ExtractData(ctx context.Context) (*DeployerData, error) { ...@@ -283,5 +285,8 @@ func (d *Deployer) ExtractData(ctx context.Context) (*DeployerData, error) {
return nil, err return nil, err
} }
return &DeployerData{State: state, Wallets: knownWallets}, nil return &DeployerData{
State: state,
Wallets: knownWallets,
}, nil
} }
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/artifact"
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -33,15 +34,15 @@ func getMnemonics(r io.Reader) (string, error) { ...@@ -33,15 +34,15 @@ func getMnemonics(r io.Reader) (string, error) {
return config[0].Mnemonic, nil return config[0].Mnemonic, nil
} }
func (d *Deployer) getKnownWallets(ctx context.Context, fs *EnclaveFS) ([]*Wallet, error) { func (d *Deployer) getKnownWallets(ctx context.Context, fs *artifact.EnclaveFS) ([]*Wallet, error) {
artifact, err := fs.GetArtifact(ctx, d.genesisArtifactName) a, err := fs.GetArtifact(ctx, d.genesisArtifactName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mnemonicsBuffer := bytes.NewBuffer(nil) mnemonicsBuffer := bytes.NewBuffer(nil)
if err := artifact.ExtractFiles( if err := a.ExtractFiles(
&ArtifactFileWriter{path: d.mnemonicsName, writer: mnemonicsBuffer}, artifact.NewArtifactFileWriter(d.mnemonicsName, mnemonicsBuffer),
); err != nil { ); err != nil {
return nil, err return nil, err
} }
......
package kurtosis package interfaces
import ( import (
"context" "context"
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/jwt"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
) )
...@@ -20,3 +21,7 @@ type EnclaveInspecter interface { ...@@ -20,3 +21,7 @@ type EnclaveInspecter interface {
type EnclaveObserver interface { type EnclaveObserver interface {
EnclaveObserve(context.Context, string) (*deployer.DeployerData, error) EnclaveObserve(context.Context, string) (*deployer.DeployerData, error)
} }
type JWTExtractor interface {
ExtractData(context.Context, string) (*jwt.Data, error)
}
package main
import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/jwt"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/urfave/cli/v2"
)
var (
GitCommit = ""
GitDate = ""
)
func main() {
app := cli.NewApp()
app.Version = fmt.Sprintf("%s-%s", GitCommit, GitDate)
app.Name = "jwt"
app.Usage = "Tool to extract JWT secrets from Kurtosis enclaves"
app.Flags = cliapp.ProtectFlags([]cli.Flag{
&cli.StringFlag{
Name: "enclave",
Usage: "Name of the Kurtosis enclave",
Required: true,
},
})
app.Action = runJWT
app.Writer = os.Stdout
app.ErrWriter = os.Stderr
err := app.Run(os.Args)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Application failed: %v\n", err)
os.Exit(1)
}
}
func runJWT(ctx *cli.Context) error {
enclave := ctx.String("enclave")
extractor := jwt.NewExtractor(enclave)
data, err := extractor.ExtractData(ctx.Context)
if err != nil {
return fmt.Errorf("failed to extract JWT data: %w", err)
}
// Print the JWT secrets
fmt.Printf("L1 JWT Secret: %s\n", data.L1JWT)
fmt.Printf("L2 JWT Secret: %s\n", data.L2JWT)
return nil
}
package jwt
import (
"bytes"
"context"
"fmt"
"io"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/artifact"
)
const (
jwtSecretFileName = "jwtsecret"
)
// Data holds the JWT secrets for L1 and L2
type Data struct {
L1JWT string
L2JWT string
}
// extractor implements the interfaces.JWTExtractor interface
type extractor struct {
enclave string
}
// NewExtractor creates a new JWT extractor
func NewExtractor(enclave string) *extractor {
return &extractor{
enclave: enclave,
}
}
// ExtractData extracts JWT secrets from their respective artifacts
func (e *extractor) ExtractData(ctx context.Context) (*Data, error) {
fs, err := artifact.NewEnclaveFS(ctx, e.enclave)
if err != nil {
return nil, err
}
// Get L1 JWT
l1JWT, err := extractJWTFromArtifact(ctx, fs, "jwt_file")
if err != nil {
return nil, fmt.Errorf("failed to get L1 JWT: %w", err)
}
// Get L2 JWT
l2JWT, err := extractJWTFromArtifact(ctx, fs, "op_jwt_file")
if err != nil {
return nil, fmt.Errorf("failed to get L2 JWT: %w", err)
}
return &Data{
L1JWT: l1JWT,
L2JWT: l2JWT,
}, nil
}
func extractJWTFromArtifact(ctx context.Context, fs *artifact.EnclaveFS, artifactName string) (string, error) {
a, err := fs.GetArtifact(ctx, artifactName)
if err != nil {
return "", fmt.Errorf("failed to get artifact: %w", err)
}
buffer := &bytes.Buffer{}
if err := a.ExtractFiles(artifact.NewArtifactFileWriter(jwtSecretFileName, buffer)); err != nil {
return "", fmt.Errorf("failed to extract JWT: %w", err)
}
return parseJWT(buffer)
}
func parseJWT(r io.Reader) (string, error) {
data, err := io.ReadAll(r)
if err != nil {
return "", fmt.Errorf("failed to read JWT file: %w", err)
}
return string(data), nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment