Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Echo App end-to-end test #358

Merged
merged 1 commit into from
Apr 12, 2024
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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [unreleased]

### Added

- Added Rollups end-to-end test using Echo Dapp

## [1.4.0] 2024-04-09

### Added
Expand All @@ -14,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added verification to ensure the Cartesi Machine snapshot hash matches the template hash from the CartesiDApp contract
- Added support for `CARTESI_AUTH_PRIVATE_KEY` and `CARTESI_AUTH_PRIVATE_KEY_FILE`
- Added `CARTESI_AUTH_KIND` environment variable to select the blockchain authetication method
- Added structured logging with slog. Colored logs can now be enabled with `CARTESI_LOG_PRETTY` environment variable.
- Added structured logging with slog. Colored logs can now be enabled with `CARTESI_LOG_PRETTY` environment variable

### Changed

Expand Down
24 changes: 23 additions & 1 deletion cmd/cartesi-rollups-cli/root/deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ package deps
import (
"context"
"log/slog"
"os"
"os/signal"
"syscall"

"github.com/cartesi/rollups-node/internal/deps"
"github.com/lmittmann/tint"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
)

Expand All @@ -24,6 +27,7 @@ const examples = `# Run all deps:
cartesi-rollups-cli run-deps`

var depsConfig = deps.NewDefaultDepsConfig()
var verbose = false

func init() {
Cmd.Flags().StringVar(&depsConfig.Postgres.DockerImage, "postgres-docker-image",
Expand All @@ -44,13 +48,31 @@ func init() {

Cmd.Flags().StringVar(&depsConfig.Devnet.Port, "devnet-mapped-port",
deps.DefaultDevnetPort,
"devnet local listening port number")
"Devnet local listening port number")

Cmd.Flags().StringVar(&depsConfig.Devnet.BlockTime, "devnet-block-time",
deps.DefaultBlockTime,
"Devnet mining block time")

Cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "verbose logs")
}

func run(cmd *cobra.Command, args []string) {
ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

if verbose {
// setup log
opts := &tint.Options{
Level: slog.LevelDebug,
AddSource: true,
NoColor: false || !isatty.IsTerminal(os.Stdout.Fd()),
}
handler := tint.NewHandler(os.Stdout, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
}

depsContainers, err := deps.Run(ctx, *depsConfig)
cobra.CheckErr(err)

Expand Down
32 changes: 7 additions & 25 deletions cmd/cartesi-rollups-cli/root/increasetime/increasetime.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
package increasetime

import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"context"
"log/slog"

"github.com/cartesi/rollups-node/pkg/ethutil"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -39,24 +37,8 @@ func init() {
}

func run(cmd *cobra.Command, args []string) {
client := &http.Client{}
var data = strings.NewReader(`{
"id":1337,
"jsonrpc":"2.0",
"method":"evm_increaseTime",
"params":[` + strconv.Itoa(time) + `]
}`)

req, err := http.NewRequest("POST", anvilEndpoint, data)
cobra.CheckErr(err)

req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
cobra.CheckErr(err)

defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
cobra.CheckErr(err)

fmt.Printf("%s\n", bodyText)

cobra.CheckErr(ethutil.AdvanceDevnetTime(context.Background(), anvilEndpoint, time))

slog.Info("Ok")
}
2 changes: 1 addition & 1 deletion cmd/cartesi-rollups-node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func main() {
slog.Info("Starting the Cartesi Rollups Node", "version", buildVersion, "config", config)

// create the node supervisor
supervisor, err := node.Setup(ctx, config)
supervisor, err := node.Setup(ctx, config, "")
if err != nil {
slog.Error("Node exited with an error", "error", err)
os.Exit(1)
Expand Down
4 changes: 3 additions & 1 deletion docs/cli/cartesi-rollups-cli_run-deps.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ cartesi-rollups-cli run-deps
### Options

```
--devnet-block-time string Devnet mining block time (default "1")
--devnet-docker-image string Devnet docker image name (default "cartesi/rollups-node-devnet:devel")
--devnet-mapped-port string devnet local listening port number (default "8545")
--devnet-mapped-port string Devnet local listening port number (default "8545")
-h, --help help for run-deps
--postgres-docker-image string Postgress docker image name (default "postgres:16-alpine")
--postgres-mapped-port string Postgres local listening port number (default "5432")
--postgres-password string Postgres password (default "password")
-v, --verbose verbose logs
```

### SEE ALSO
Expand Down
146 changes: 113 additions & 33 deletions internal/deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package deps
import (
"context"
"fmt"
"io"
"log/slog"
"strings"
"sync"
Expand All @@ -17,16 +18,25 @@ import (
)

const (
DefaultPostgresDockerImage = "postgres:16-alpine"
DefaultPostgresPort = "5432"
DefaultPostgresPassword = "password"
DefaultDevnetDockerImage = "cartesi/rollups-node-devnet:devel"
DefaultDevnetPort = "8545"
DefaultPostgresDatabase = "postgres"
DefaultPostgresDockerImage = "postgres:16-alpine"
DefaultPostgresPort = "5432"
DefaultPostgresUser = "postgres"
DefaultPostgresPassword = "password"
DefaultDevnetDockerImage = "cartesi/rollups-node-devnet:devel"
DefaultDevnetPort = "8545"
DefaultBlockTime = "1"
DefaultBlockToWaitForOnStartup = "21"

numPostgresCheckReadyAttempts = 2
pollInterval = 5 * time.Second
)

const (
postgresKey = iota
devnetKey
)

// Struct to hold Node dependencies containers configurations
type DepsConfig struct {
Postgres *PostgresConfig
Expand All @@ -40,8 +50,10 @@ type PostgresConfig struct {
}

type DevnetConfig struct {
DockerImage string
Port string
DockerImage string
Port string
BlockTime string
BlockToWaitForOnStartup string
}

// Builds a DepsConfig struct with default values
Expand All @@ -55,30 +67,88 @@ func NewDefaultDepsConfig() *DepsConfig {
&DevnetConfig{
DefaultDevnetDockerImage,
DefaultDevnetPort,
DefaultBlockTime,
DefaultBlockToWaitForOnStartup,
},
}
}

// Struct to represent the Node dependencies containers
type DepsContainers struct {
containers []testcontainers.Container
containers map[int]testcontainers.Container
//Literal copies lock value from waitGroup as sync.WaitGroup contains sync.noCopy
waitGroup *sync.WaitGroup
}

func (depContainers *DepsContainers) DevnetLogs(ctx context.Context) (io.ReadCloser, error) {
container, ok := depContainers.containers[devnetKey]
if !ok {
return nil, fmt.Errorf("Container Devnet is not present")
}
reader, err := container.Logs(ctx)
if err != nil {
return nil, fmt.Errorf("Error retrieving logs from Devnet Container : %w", err)
}
return reader, nil
}

func (depContainers *DepsContainers) PostgresLogs(ctx context.Context) (io.ReadCloser, error) {
container, ok := depContainers.containers[postgresKey]
if !ok {
return nil, fmt.Errorf("Container Postgres is not present")
}
reader, err := container.Logs(ctx)
if err != nil {
return nil, fmt.Errorf("Error retrieving logs from Postgres Container : %w", err)
}
return reader, nil
}

func (depContainers *DepsContainers) DevnetEndpoint(
ctx context.Context,
protocol string,
) (string, error) {
container, ok := depContainers.containers[devnetKey]
if !ok {
return "", fmt.Errorf("Container Devnet is not present")
}
endpoint, err := container.Endpoint(ctx, protocol)
if err != nil {
return "", fmt.Errorf("Error retrieving endpoint from Devnet Container : %w", err)
}
return endpoint, nil
}

func (depContainers *DepsContainers) PostgresEndpoint(
ctx context.Context,
protocol string,
) (string, error) {

container, ok := depContainers.containers[postgresKey]
if !ok {
return "", fmt.Errorf("Container Postgres is not present")
}
endpoint, err := container.Endpoint(ctx, protocol)
if err != nil {
return "", fmt.Errorf("Error retrieving endpoint from Postgres Container : %w", err)
}
return endpoint, nil
}

// debugLogging implements the testcontainers.Logging interface by printing the log to slog.Debug.
type debugLogging struct{}

func (d debugLogging) Printf(format string, v ...interface{}) {
slog.Debug(fmt.Sprintf(format, v...))
}

func createHook(waitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHooks {
func createHook(finishedWaitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHooks {
return []testcontainers.ContainerLifecycleHooks{
{

PostTerminates: []testcontainers.ContainerHook{
func(ctx context.Context, container testcontainers.Container) error {
waitGroup.Done()
finishedWaitGroup.Done()
return nil
},
},
Expand All @@ -90,25 +160,29 @@ func createHook(waitGroup *sync.WaitGroup) []testcontainers.ContainerLifecycleHo
// The returned DepContainers struct can be used to gracefully
// terminate the containers using the Terminate method
func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
debugLogger := debugLogging{}
var waitGroup sync.WaitGroup

containers := []testcontainers.Container{}
debugLogger := debugLogging{}
var finishedWaitGroup sync.WaitGroup
containers := make(map[int]testcontainers.Container)
if depsConfig.Postgres != nil {
// wait strategy copied from testcontainers docs
postgresWaitStrategy := wait.ForLog("database system is ready to accept connections").
WithOccurrence(numPostgresCheckReadyAttempts).
WithPollInterval(pollInterval)

postgresExposedPorts := "5432/tcp"
if depsConfig.Postgres.Port != "" {
postgresExposedPorts = strings.Join([]string{
depsConfig.Postgres.Port, ":", postgresExposedPorts}, "")
}
postgresReq := testcontainers.ContainerRequest{
Image: depsConfig.Postgres.DockerImage,
ExposedPorts: []string{strings.Join([]string{
depsConfig.Postgres.Port, ":5432/tcp"}, "")},
WaitingFor: postgresWaitStrategy,
Name: "rollups-node-dep-postgres",
Image: depsConfig.Postgres.DockerImage,
ExposedPorts: []string{postgresExposedPorts},
WaitingFor: postgresWaitStrategy,
Env: map[string]string{
"POSTGRES_PASSWORD": depsConfig.Postgres.Password,
},
LifecycleHooks: createHook(&waitGroup),
LifecycleHooks: createHook(&finishedWaitGroup),
}
postgres, err := testcontainers.GenericContainer(
ctx,
Expand All @@ -121,20 +195,24 @@ func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
if err != nil {
return nil, err
}
waitGroup.Add(1)
containers = append(containers, postgres)
finishedWaitGroup.Add(1)
containers[postgresKey] = postgres
}

if depsConfig.Devnet != nil {

devnetExposedPort := "8545/tcp"
if depsConfig.Devnet.Port != "" {
devnetExposedPort = strings.Join([]string{
depsConfig.Devnet.Port, ":", devnetExposedPort}, "")
}
devNetReq := testcontainers.ContainerRequest{
Image: depsConfig.Devnet.DockerImage,
ExposedPorts: []string{strings.Join([]string{depsConfig.Devnet.Port, ":8545/tcp"}, "")},
WaitingFor: wait.ForLog("Listening on 0.0.0.0:8545"),
Name: "rollups-node-dep-devnet",
Env: map[string]string{
"ANVIL_IP_ADDR": "0.0.0.0",
},
LifecycleHooks: createHook(&waitGroup),
ExposedPorts: []string{devnetExposedPort},
WaitingFor: wait.ForLog("Block Number: " + depsConfig.Devnet.BlockToWaitForOnStartup),
Cmd: []string{"anvil", "--block-time",
depsConfig.Devnet.BlockTime, "--load-state", "/usr/share/devnet/anvil_state.json"},
LifecycleHooks: createHook(&finishedWaitGroup),
}
devnet, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: devNetReq,
Expand All @@ -144,25 +222,27 @@ func Run(ctx context.Context, depsConfig DepsConfig) (*DepsContainers, error) {
if err != nil {
return nil, err
}
waitGroup.Add(1)
containers = append(containers, devnet)
finishedWaitGroup.Add(1)
containers[devnetKey] = devnet
}

if len(containers) < 1 {
return nil, fmt.Errorf("configuration is empty")
}
return &DepsContainers{containers, &waitGroup}, nil

return &DepsContainers{containers: containers,
waitGroup: &finishedWaitGroup,
}, nil
}

// Terminate terminates all dependencies containers. This method waits for all the containers
// to terminate or gives an error if it fails to terminate one of the containers
func Terminate(ctx context.Context, depContainers *DepsContainers) error {

for _, depContainer := range depContainers.containers {
terr := depContainer.Terminate(ctx)
if terr != nil {
return terr
}
}
depContainers.waitGroup.Wait()
return nil
}
Loading
Loading