Skip to content
Merged
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
4 changes: 4 additions & 0 deletions pkg/ethereum/ethereum_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (

// NewEthereumNodeComponent creates a new Ethereum node component that combines an execution client and a consensus client
func NewEthereumNodeComponent(ctx *pulumi.Context, args *EthereumNodeArgs, opts ...pulumi.ResourceOption) (*EthereumNodeComponent, error) {
if err := args.Validate(); err != nil {
return nil, fmt.Errorf("invalid ethereum node args: %w", err)
}

component := &EthereumNodeComponent{
Name: args.Name,
Namespace: args.Namespace,
Expand Down
36 changes: 36 additions & 0 deletions pkg/ethereum/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ethereum

import (
"fmt"
)

// Validate validates the EthereumNodeArgs struct
func (args *EthereumNodeArgs) Validate() error {
if args.Name == "" {
return fmt.Errorf("name is required")
}

if args.Namespace == "" {
return fmt.Errorf("namespace is required")
}

if args.ExecutionClient == nil {
return fmt.Errorf("executionClient is required")
}

if args.ConsensusClient == nil {
return fmt.Errorf("consensusClient is required")
}

// Validate execution client
if err := args.ExecutionClient.Validate(); err != nil {
return fmt.Errorf("execution client validation failed: %w", err)
}

// Validate consensus client
if err := args.ConsensusClient.Validate(); err != nil {
return fmt.Errorf("consensus client validation failed: %w", err)
}

return nil
}
122 changes: 122 additions & 0 deletions pkg/ethereum/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package ethereum

import (
"testing"

"github.com/init4tech/signet-infra-components/pkg/ethereum/consensus"
"github.com/init4tech/signet-infra-components/pkg/ethereum/execution"
"github.com/stretchr/testify/assert"
)

func TestEthereumNodeArgsValidate(t *testing.T) {
// Test with valid args
validArgs := EthereumNodeArgs{
Name: "test-node",
Namespace: "default",
ExecutionClient: &execution.ExecutionClientArgs{
Name: "test-execution",
Namespace: "default",
StorageSize: "100Gi",
StorageClass: "standard",
Image: "test-execution-image",
ImagePullPolicy: "Always",
JWTSecret: "test-jwt-secret",
P2PPort: 30303,
RPCPort: 8545,
WSPort: 8546,
MetricsPort: 9090,
AuthRPCPort: 8551,
DiscoveryPort: 30303,
},
ConsensusClient: &consensus.ConsensusClientArgs{
Name: "test-consensus",
Namespace: "default",
StorageSize: "100Gi",
StorageClass: "standard",
Image: "test-consensus-image",
ImagePullPolicy: "Always",
JWTSecret: "test-jwt-secret",
P2PPort: 30303,
BeaconAPIPort: 5052,
MetricsPort: 9090,
ExecutionClientEndpoint: "http://execution:8551",
},
}

err := validArgs.Validate()
assert.NoError(t, err)

// Test with missing name
invalidArgs1 := EthereumNodeArgs{
Namespace: "default",
ExecutionClient: validArgs.ExecutionClient,
ConsensusClient: validArgs.ConsensusClient,
}

err = invalidArgs1.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "name is required")

// Test with missing namespace
invalidArgs2 := EthereumNodeArgs{
Name: "test-node",
ExecutionClient: validArgs.ExecutionClient,
ConsensusClient: validArgs.ConsensusClient,
}

err = invalidArgs2.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "namespace is required")

// Test with missing execution client
invalidArgs3 := EthereumNodeArgs{
Name: "test-node",
Namespace: "default",
ConsensusClient: validArgs.ConsensusClient,
}

err = invalidArgs3.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "executionClient is required")

// Test with missing consensus client
invalidArgs4 := EthereumNodeArgs{
Name: "test-node",
Namespace: "default",
ExecutionClient: validArgs.ExecutionClient,
}

err = invalidArgs4.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "consensusClient is required")

// Test with invalid execution client
invalidExecutionClient := &execution.ExecutionClientArgs{
// Missing required fields
}
invalidArgs5 := EthereumNodeArgs{
Name: "test-node",
Namespace: "default",
ExecutionClient: invalidExecutionClient,
ConsensusClient: validArgs.ConsensusClient,
}

err = invalidArgs5.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "execution client validation failed")

// Test with invalid consensus client
invalidConsensusClient := &consensus.ConsensusClientArgs{
// Missing required fields
}
invalidArgs6 := EthereumNodeArgs{
Name: "test-node",
Namespace: "default",
ExecutionClient: validArgs.ExecutionClient,
ConsensusClient: invalidConsensusClient,
}

err = invalidArgs6.Validate()
assert.Error(t, err)
assert.Contains(t, err.Error(), "consensus client validation failed")
}
25 changes: 25 additions & 0 deletions pkg/pylon/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package pylon

// Storage constants
const (
ExecutionClientStorageSize = "150Gi"
ConsensusClientStorageSize = "100Gi"
StorageClassAWSGP3 = "aws-gp3"
)

// Port constants
const (
ExecutionP2PPort = 30303
ExecutionRPCPort = 8545
ExecutionWSPort = 8546
ExecutionMetricsPort = 9001
ExecutionAuthRPCPort = 8551
ConsensusBeaconAPIPort = 4000
ConsensusMetricsPort = 5054
)

// Image constants
const (
ConsensusClientImage = "sigp/lighthouse:latest"
ImagePullPolicyAlways = "Always"
)
1 change: 1 addition & 0 deletions pkg/pylon/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package pylon
Copy link

Copilot AI Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty and can be removed to avoid clutter.

Copilot uses AI. Check for mistakes.
93 changes: 93 additions & 0 deletions pkg/pylon/pylon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package pylon

import (
"fmt"

"github.com/init4tech/signet-infra-components/pkg/ethereum"
"github.com/init4tech/signet-infra-components/pkg/ethereum/consensus"
"github.com/init4tech/signet-infra-components/pkg/ethereum/execution"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func NewPylonComponent(ctx *pulumi.Context, args *PylonComponentArgs, opts ...pulumi.ResourceOption) (*PylonComponent, error) {
if err := args.Validate(); err != nil {
return nil, fmt.Errorf("invalid pylon component args: %w", err)
}

component := &PylonComponent{}

err := ctx.RegisterComponentResource("signet:index:Pylon", args.Name, component, opts...)
if err != nil {
return nil, err
}

// Convert public args to internal args
internalArgs := args.toInternal()

stack := ctx.Stack()

// Get the existing Route53 hosted zone for signet.sh
dbProjectName := internalArgs.DbProjectName
dbStackName := fmt.Sprintf("%s/%s", dbProjectName, stack)

// TODO: this should be a stack reference to the pylon db stack- should this be an arg?
// Need to think about how i want to handle the separation of the pylon db and pylon components
thePylonDbStack, err := pulumi.NewStackReference(ctx, dbStackName, nil)
if err != nil {
return nil, err
}

// Get the database cluster endpoint (unused for now but needed for future implementation)
_ = thePylonDbStack.GetStringOutput(pulumi.String("dbClusterEndpoint"))

// Create the S3 bucket for blob storage (unused for now but needed for future implementation)
_, err = s3.NewBucketV2(ctx, "pylon-blob-bucket", &s3.BucketV2Args{
Bucket: internalArgs.PylonBlobBucketName,
})
if err != nil {
return nil, err
}

// Convert environment to internal type for use with ethereum components
internalEnv := args.Env.toInternal()

// Create Ethereum node component
ethereumNodeArgs := &ethereum.EthereumNodeArgs{
Name: args.Name,
Namespace: args.Namespace,
ExecutionClient: &execution.ExecutionClientArgs{
Name: args.Name,
Namespace: args.Namespace,
StorageSize: ExecutionClientStorageSize,
StorageClass: StorageClassAWSGP3,
Image: args.PylonImage,
JWTSecret: args.ExecutionJwt,
P2PPort: ExecutionP2PPort,
RPCPort: ExecutionRPCPort,
WSPort: ExecutionWSPort,
MetricsPort: ExecutionMetricsPort,
AuthRPCPort: ExecutionAuthRPCPort,
DiscoveryPort: ExecutionP2PPort,
ExecutionClientEnv: internalEnv,
},
ConsensusClient: &consensus.ConsensusClientArgs{
Name: args.Name,
Namespace: args.Namespace,
StorageSize: ConsensusClientStorageSize,
StorageClass: StorageClassAWSGP3,
Image: ConsensusClientImage,
ImagePullPolicy: ImagePullPolicyAlways,
BeaconAPIPort: ConsensusBeaconAPIPort,
MetricsPort: ConsensusMetricsPort,
},
}

ethereumNode, err := ethereum.NewEthereumNodeComponent(ctx, ethereumNodeArgs, pulumi.Parent(component))
if err != nil {
return nil, err
}
component.EthereumNode = ethereumNode

return component, nil
}
Loading
Loading