diff --git a/build/Dockerfile b/build/Dockerfile index 7bd645ed3..8f5774b5d 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -111,7 +111,7 @@ USER root ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ - libpq5 ca-certificates curl redis-tools + libpq5 ca-certificates curl redis-tools redis RUN rm -rf /var/lib/apt/lists/* # Copy Rust binaries @@ -132,4 +132,4 @@ RUN chown cartesi:cartesi ${RUNTIME_DIR} WORKDIR ${RUNTIME_DIR} USER cartesi -CMD ["/bin/bash"] +CMD ["cartesi-rollups-node"] diff --git a/build/deps-compose.yml b/build/deps-compose.yml new file mode 100644 index 000000000..e2d55c2b3 --- /dev/null +++ b/build/deps-compose.yml @@ -0,0 +1,72 @@ +version: "3.9" + +name: rollups-node +services: + devnet: + image: sunodo/devnet:1.1.1 + command: + [ + "anvil", + "--block-time", + "${BLOCK_TIME:-5}", + "--load-state", + "/usr/share/sunodo/anvil_state.json", + ] + ports: + - 8545:8545 + healthcheck: + test: ["CMD", "eth_isready"] + interval: 10s + timeout: 1s + retries: 5 + environment: + ANVIL_IP_ADDR: 0.0.0.0 + volumes: + - blockchain-data:/usr/share/sunodo + + machine_snapshot_setup: + image: cartesi/rollups-machine-snapshot:devel + volumes: + - machine:/var/opt/cartesi/machine-snapshots + + dapp_deployer: + image: cartesi/rollups-cli:1.0.2 + restart: on-failure + depends_on: + devnet: + condition: service_started + machine_snapshot_setup: + condition: service_completed_successfully + command: + [ + "create", + "--rpc", + "http://devnet:8545", + "--deploymentFile", + "/usr/share/sunodo/localhost.json", + "--mnemonic", + "test test test test test test test test test test test junk", + "--templateHashFile", + "/var/opt/cartesi/machine-snapshots/0_0/hash", + "--outputFile", + "/usr/share/sunodo/dapp.json", + ] + volumes: + - machine:/var/opt/cartesi/machine-snapshots:ro + - blockchain-data:/usr/share/sunodo + + database: + image: postgres:13-alpine + ports: + - 5432:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"] + interval: 10s + timeout: 5s + retries: 5 + environment: + - POSTGRES_PASSWORD=password + +volumes: + blockchain-data: {} + machine: {} diff --git a/build/docker-compose.yml b/build/docker-compose.yml deleted file mode 100644 index 685faa634..000000000 --- a/build/docker-compose.yml +++ /dev/null @@ -1,206 +0,0 @@ -version: "3.9" - -name: rollups-node -services: - node: - image: "cartesi/rollups-node:devel" - build: - context: .. - dockerfile: build/Dockerfile - entrypoint: ["cartesi-rollups-node", "validator"] - ports: - - "4000:4000" - - "5005:5005" - depends_on: - hardhat: - condition: service_healthy - deployer: - condition: service_completed_successfully - hardhat_set_interval: - condition: service_completed_successfully - machine_snapshot_setup: - condition: service_completed_successfully - database: - condition: service_healthy - redis: - condition: service_healthy - environment: - RUST_LOG: info - CARTESI_LOG_LEVEL: info - POSTGRES_ENDPOINT: postgres://postgres:password@database:5432/postgres - CHAIN_ID: 31337 # dispacher + indexer - - #Advance Runner - ADVANCE_RUNNER_HEALTHCHECK_PORT: 8080 - SERVER_MANAGER_ENDPOINT: http://0.0.0.0:5001 - PROVIDER_HTTP_ENDPOINT: http://hardhat:8545 - SESSION_ID: default_rollups_id - SNAPSHOT_DIR: /var/opt/cartesi/machine-snapshots - SNAPSHOT_LATEST: /var/opt/cartesi/machine-snapshots/latest - - # Authority Claimer - AUTHORITY_CLAIMER_HTTP_SERVER_PORT: 8085 - TX_PROVIDER_HTTP_ENDPOINT: http://hardhat:8545 - TX_CHAIN_ID: 31337 - TX_CHAIN_IS_LEGACY: ${TX_LEGACY:-false} - TX_DEFAULT_CONFIRMATIONS: 2 - TX_SIGNING_MNEMONIC: "test test test test test test test test test test test junk" - - # Dispatcher - DISPATCHER_HTTP_SERVER_PORT: 8081 - DAPP_DEPLOYMENT_FILE: /deployments/localhost/dapp.json - ROLLUPS_DEPLOYMENT_FILE: /opt/cartesi/share/deployments/localhost.json - RD_EPOCH_DURATION: 86400 - SC_GRPC_ENDPOINT: http://0.0.0.0:50051 - SC_DEFAULT_CONFIRMATIONS: 1 - - # GraphQL Server - GRAPHQL_HEALTHCHECK_PORT: 8082 - GRAPHQL_HOST: "0.0.0.0" - GRAPHQL_PORT: "4000" - - # Indexer - INDEXER_HEALTHCHECK_PORT: 8083 - DAPP_CONTRACT_ADDRESS_FILE: /deployments/localhost/dapp.json - REDIS_ENDPOINT: redis://redis:6379 - - # Inspect Server - INSPECT_SERVER_HEALTHCHECK_PORT: 8084 - INSPECT_SERVER_ADDRESS: 0.0.0.0:5005 - SERVER_MANAGER_ADDRESS: 0.0.0.0:5001 - - # State Server - SS_SERVER_ADDRESS: 0.0.0.0:50051 - SF_GENESIS_BLOCK: 0x1 - SF_SAFETY_MARGIN: 1 - BH_HTTP_ENDPOINT: http://hardhat:8545 - BH_WS_ENDPOINT: ws://hardhat:8545 - BH_BLOCK_TIMEOUT: 8 - - # Server Manager - MANAGER_ADDRESS: http://0.0.0.0:5001 - SERVER_MANAGER_LOG_LEVEL: warning - REMOTE_CARTESI_MACHINE_LOG_LEVEL: info - - volumes: - - machine:/var/opt/cartesi/machine-snapshots - - blockchain-data:/opt/cartesi/share/deployments:ro - - ./deployments:/deployments:ro - - hardhat: - image: cartesi/rollups-hardhat:1.0.0 - command: - [ - "node", - "--network", - "hardhat", - "--export", - "/opt/cartesi/share/deployments/localhost.json", - ] - init: true - ports: - - "8545:8545" - healthcheck: - test: - ["CMD", "test", "-f", "/opt/cartesi/share/deployments/localhost.json"] - interval: 30s - timeout: 30s - retries: 5 - volumes: - - blockchain-data:/opt/cartesi/share/deployments - - ./deployments:/app/rollups/deployments - - deployer: - image: cartesi/rollups-cli:1.0.0 - restart: on-failure - depends_on: - hardhat: - condition: service_healthy - machine_snapshot_setup: - condition: service_completed_successfully - command: - [ - "create", - "--rpc", - "http://hardhat:8545", - "--deploymentFile", - "/opt/cartesi/share/deployments/localhost.json", - "--mnemonic", - "test test test test test test test test test test test junk", - "--templateHashFile", - "/var/opt/cartesi/machine-snapshots/0_0/hash", - "--outputFile", - "/deployments/localhost/dapp.json", - ] - volumes: - - blockchain-data:/opt/cartesi/share/deployments:ro - - machine:/var/opt/cartesi/machine-snapshots:ro - - ./deployments:/deployments - - hardhat_stop_automine: - image: curlimages/curl:7.84.0 - restart: on-failure - depends_on: - hardhat: - condition: service_healthy - deployer: - condition: service_completed_successfully - command: - [ - "--data", - '{"id":1337,"jsonrpc":"2.0","method":"evm_setAutomine","params":[false]}', - "http://hardhat:8545", - ] - - hardhat_set_interval: - image: curlimages/curl:7.84.0 - restart: on-failure - depends_on: - hardhat: - condition: service_healthy - hardhat_stop_automine: - condition: service_completed_successfully - command: - [ - "--data", - '{"id":1337,"jsonrpc":"2.0","method":"evm_setIntervalMining","params":[5000]}', - "http://hardhat:8545", - ] - - machine_snapshot_setup: - image: cartesi/rollups-machine-snapshot:devel - volumes: - - machine:/var/opt/cartesi/machine-snapshots - - database: - image: postgres:13-alpine - ports: - - 5432:5432 - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"] - interval: 10s - timeout: 5s - retries: 5 - environment: - - POSTGRES_PASSWORD=password - volumes: - - database-data:/var/lib/postgresql/data - - redis: - image: redis:6-alpine - ports: - - 6379:6379 - restart: always - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 5s - retries: 5 - volumes: - - redis-data:/data - -volumes: - blockchain-data: {} - machine: {} - database-data: {} - redis-data: {} diff --git a/build/node-compose.yml b/build/node-compose.yml new file mode 100644 index 000000000..d66605b46 --- /dev/null +++ b/build/node-compose.yml @@ -0,0 +1,41 @@ +version: "3.9" + +name: rollups-node +services: + node: + image: "cartesi/rollups-node:devel" + ports: + - "10003:4000" + - "10007:5005" + restart: always + depends_on: + devnet: + condition: service_healthy + dapp_deployer: + condition: service_completed_successfully + machine_snapshot_setup: + condition: service_completed_successfully + database: + condition: service_healthy + environment: + CARTESI_LOG_LEVEL: "info" + CARTESI_LOG_TIMESTAMP: "true" + CARTESI_FEATURE_HOST_MODE: "false" + CARTESI_FEATURE_READER_MODE: "false" + CARTESI_EPOCH_DURATION: "120" + CARTESI_BLOCKCHAIN_ID: "31337" + CARTESI_BLOCKCHAIN_HTTP_ENDPOINT: "http://devnet:8545" + CARTESI_BLOCKCHAIN_WS_ENDPOINT: "ws://devnet:8545" + CARTESI_BLOCKCHAIN_IS_LEGACY: "false" + CARTESI_BLOCKCHAIN_GENESIS_BLOCK: "1" + CARTESI_BLOCKCHAIN_READ_DEPTH: "1" + CARTESI_CONTRACTS_DAPP_ADDRESS: "0x70ac08179605AF2D9e75782b8DEcDD3c22aA4D0C" + CARTESI_CONTRACTS_DAPP_DEPLOYMENT_BLOCK_NUMBER: "1" + CARTESI_CONTRACTS_HISTORY_ADDRESS: "0x4FF8BD9122b7D91d56Dd5c88FE6891Fb3c0b5281" + CARTESI_CONTRACTS_AUTHORITY_ADDRESS: "0x5050F233F2312B1636eb7CF6c7876D9cC6ac4785" + CARTESI_CONTRACTS_INPUT_BOX_ADDRESS: "0x59b22D57D4f067708AB0c00552767405926dc768" + CARTESI_SNAPSHOT_DIR: "/var/opt/cartesi/machine-snapshots" + CARTESI_AUTH_MNEMONIC: "test test test test test test test test test test test junk" + CARTESI_POSTGRES_ENDPOINT: "postgres://postgres:password@database:5432/postgres" + volumes: + - machine:/var/opt/cartesi/machine-snapshots diff --git a/cmd/cartesi-rollups-cli/root/send/send.go b/cmd/cartesi-rollups-cli/root/send/send.go index a0d5d061e..81cc5d667 100644 --- a/cmd/cartesi-rollups-cli/root/send/send.go +++ b/cmd/cartesi-rollups-cli/root/send/send.go @@ -50,8 +50,6 @@ func init() { } func run(cmd *cobra.Command, args []string) { - logger.Init("info", true) - payload, err := hexutil.Decode(hexPayload) cobra.CheckErr(err) diff --git a/cmd/cartesi-rollups-node/main.go b/cmd/cartesi-rollups-node/main.go index 2ef677882..0c322f926 100644 --- a/cmd/cartesi-rollups-node/main.go +++ b/cmd/cartesi-rollups-node/main.go @@ -5,18 +5,38 @@ package main import ( "context" - "os" - "github.com/cartesi/rollups-node/internal/logger" + "github.com/cartesi/rollups-node/internal/config" + "github.com/cartesi/rollups-node/internal/services" ) func main() { - logLevel := os.Getenv("CARTESI_LOG_LEVEL") - _, enableTimestamp := os.LookupEnv("CARTESI_LOG_ENABLE_TIMESTAMP") - logger.Init(logLevel, enableTimestamp) + var s []services.Service - ctx := context.Background() - if err := rootCmd.ExecuteContext(ctx); err != nil { - logger.Error.Panic(err) + // Start Redis first + s = append(s, newRedis()) + + // Start services without dependencies + s = append(s, newGraphQLServer()) + s = append(s, newIndexer()) + s = append(s, newStateServer()) + + // Start either the server manager or host runner + if config.GetFeatureHostMode() { + s = append(s, newHostRunner()) + } else { + s = append(s, newServerManager()) + } + + // Enable claimer if reader mode is disabled + if !config.GetFeatureReaderMode() { + s = append(s, newAuthorityClaimer()) } + + // Start services with dependencies + s = append(s, newAdvanceRunner()) // Depends on the server-manager/host-runner + s = append(s, newDispatcher()) // Depends on the state server + s = append(s, newInspectServer()) // Depends on the server-manager/host-runner + + services.Run(context.Background(), s) } diff --git a/cmd/cartesi-rollups-node/nobackend.go b/cmd/cartesi-rollups-node/nobackend.go deleted file mode 100644 index 9684d0d14..000000000 --- a/cmd/cartesi-rollups-node/nobackend.go +++ /dev/null @@ -1,15 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -package main - -import "github.com/spf13/cobra" - -var noBackend = &cobra.Command{ - Use: "nobackend", - Short: "Starts the node in nobackend mode", - DisableFlagsInUseLine: true, - Run: func(cmd *cobra.Command, args []string) { - println("TODO") - }, -} diff --git a/cmd/cartesi-rollups-node/reader.go b/cmd/cartesi-rollups-node/reader.go deleted file mode 100644 index dc4f1642c..000000000 --- a/cmd/cartesi-rollups-node/reader.go +++ /dev/null @@ -1,15 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -package main - -import "github.com/spf13/cobra" - -var reader = &cobra.Command{ - Use: "reader", - Short: "Starts the node in reader mode", - DisableFlagsInUseLine: true, - Run: func(cmd *cobra.Command, args []string) { - println("TODO") - }, -} diff --git a/cmd/cartesi-rollups-node/root.go b/cmd/cartesi-rollups-node/root.go deleted file mode 100644 index e9db60aab..000000000 --- a/cmd/cartesi-rollups-node/root.go +++ /dev/null @@ -1,18 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -package main - -import "github.com/spf13/cobra" - -var rootCmd = &cobra.Command{ - Use: "cartesi-rollups-node", - CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true}, - DisableFlagsInUseLine: true, -} - -func init() { - rootCmd.AddCommand(reader) - rootCmd.AddCommand(validator) - rootCmd.AddCommand(noBackend) -} diff --git a/cmd/cartesi-rollups-node/services.go b/cmd/cartesi-rollups-node/services.go new file mode 100644 index 000000000..4660332ca --- /dev/null +++ b/cmd/cartesi-rollups-node/services.go @@ -0,0 +1,299 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +package main + +import ( + "fmt" + "os" + + "github.com/cartesi/rollups-node/internal/config" + "github.com/cartesi/rollups-node/internal/services" +) + +// We use iota to define the ports of each service and avoid conflicts. +const ( + advanceRunnerPort = 10000 + iota + authorityClaimerPort + dispatcherPort + graphQLServerPort + graphQLHealthcheckPort + hostRunnerHealthcheckPort + hostRunnerRollupsPort + indexerPort + inspectServerPort + inspectHealthcheckPort + redisPort + serverManagerPort + stateServerPort +) + +const serverManagerSessionId = "default_session_id" + +// Create the RUST_LOG variable using the config log level. +// If the log level is set to debug, set tracing log for the given rust module. +func getRustLog(rustModule string) string { + switch config.GetLogLevel() { + case config.LogLevelDebug: + return fmt.Sprintf("RUST_LOG=info,%v=trace", rustModule) + case config.LogLevelInfo: + return "RUST_LOG=info" + case config.LogLevelWarning: + return "RUST_LOG=warn" + case config.LogLevelError: + return "RUST_LOG=error" + default: + panic("impossible") + } +} + +func newAdvanceRunner() services.Service { + var s services.Service + s.Name = "advance-runner" + s.HealthcheckPort = advanceRunnerPort + s.Path = "cartesi-rollups-advance-runner" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("advance_runner")) + s.Env = append(s.Env, + fmt.Sprintf("SERVER_MANAGER_ENDPOINT=http://127.0.0.1:%v", serverManagerPort)) + s.Env = append(s.Env, + fmt.Sprintf("SESSION_ID=%v", serverManagerSessionId)) + s.Env = append(s.Env, + fmt.Sprintf("REDIS_ENDPOINT=redis://127.0.0.1:%v", redisPort)) + s.Env = append(s.Env, + fmt.Sprintf("CHAIN_ID=%v", config.GetBlockchainId())) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_CONTRACT_ADDRESS=%v", config.GetContractsDappAddress())) + s.Env = append(s.Env, + fmt.Sprintf("PROVIDER_HTTP_ENDPOINT=%v", config.GetBlockchainHttpEndpoint())) + s.Env = append(s.Env, + fmt.Sprintf("ADVANCE_RUNNER_HEALTHCHECK_PORT=%v", advanceRunnerPort)) + if config.GetFeatureHostMode() { + s.Env = append(s.Env, "SNAPSHOT_ENABLED=false") + s.Env = append(s.Env, "SNAPSHOT_VALIDATION_ENABLED=false") + } else { + s.Env = append(s.Env, + fmt.Sprintf("SNAPSHOT_DIR=%v", config.GetSnapshotDir())) + s.Env = append(s.Env, + fmt.Sprintf("SNAPSHOT_LATEST=%v/latest", config.GetSnapshotDir())) + } + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newAuthorityClaimer() services.Service { + var s services.Service + s.Name = "authority-claimer" + s.HealthcheckPort = authorityClaimerPort + s.Path = "cartesi-rollups-authority-claimer" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("authority_claimer")) + s.Env = append(s.Env, + fmt.Sprintf("TX_PROVIDER_HTTP_ENDPOINT=%v", config.GetBlockchainHttpEndpoint())) + s.Env = append(s.Env, + fmt.Sprintf("TX_CHAIN_ID=%v", config.GetBlockchainId())) + s.Env = append(s.Env, + fmt.Sprintf("TX_CHAIN_IS_LEGACY=%v", config.GetBlockchainIsLegacy())) + s.Env = append(s.Env, + fmt.Sprintf("TX_DEFAULT_CONFIRMATIONS=%v", config.GetBlockchainReadDepth())) + s.Env = append(s.Env, + fmt.Sprintf("REDIS_ENDPOINT=redis://127.0.0.1:%v", redisPort)) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_ADDRESS=%v", config.GetContractsDappAddress())) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_DEPLOYMENT_BLOCK_NUMBER=%v", + config.GetContractsDappDeploymentBlockNumber())) + s.Env = append(s.Env, + fmt.Sprintf("HISTORY_ADDRESS=%v", config.GetContractsHistoryAddress())) + s.Env = append(s.Env, + fmt.Sprintf("AUTHORITY_ADDRESS=%v", config.GetContractsAuthorityAddress())) + s.Env = append(s.Env, + fmt.Sprintf("INPUT_BOX_ADDRESS=%v", config.GetContractsInputBoxAddress())) + s.Env = append(s.Env, + fmt.Sprintf("GENESIS_BLOCK=%v", config.GetBlockchainGenesisBlock())) + s.Env = append(s.Env, + fmt.Sprintf("AUTHORITY_CLAIMER_HTTP_SERVER_PORT=%v", authorityClaimerPort)) + switch auth := config.GetAuth().(type) { + case config.AuthMnemonic: + s.Env = append(s.Env, + fmt.Sprintf("TX_SIGNING_MNEMONIC=%v", auth.Mnemonic)) + s.Env = append(s.Env, + fmt.Sprintf("TX_SIGNING_MNEMONIC_ACCOUNT_INDEX=%v", auth.AccountIndex)) + case config.AuthAWS: + s.Env = append(s.Env, + fmt.Sprintf("TX_SIGNING_AWS_KMS_KEY_ID=%v", auth.KeyID)) + s.Env = append(s.Env, + fmt.Sprintf("TX_SIGNING_AWS_KMS_REGION=%v", auth.Region)) + default: + panic("invalid auth config") + } + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newDispatcher() services.Service { + var s services.Service + s.Name = "dispatcher" + s.HealthcheckPort = dispatcherPort + s.Path = "cartesi-rollups-dispatcher" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("dispatcher")) + s.Env = append(s.Env, + fmt.Sprintf("SC_GRPC_ENDPOINT=http://127.0.0.1:%v", stateServerPort)) + s.Env = append(s.Env, + fmt.Sprintf("SC_DEFAULT_CONFIRMATIONS=%v", config.GetBlockchainReadDepth())) + s.Env = append(s.Env, + fmt.Sprintf("REDIS_ENDPOINT=redis://127.0.0.1:%v", redisPort)) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_ADDRESS=%v", config.GetContractsDappAddress())) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_DEPLOYMENT_BLOCK_NUMBER=%v", + config.GetContractsDappDeploymentBlockNumber())) + s.Env = append(s.Env, + fmt.Sprintf("HISTORY_ADDRESS=%v", config.GetContractsHistoryAddress())) + s.Env = append(s.Env, + fmt.Sprintf("AUTHORITY_ADDRESS=%v", config.GetContractsAuthorityAddress())) + s.Env = append(s.Env, + fmt.Sprintf("INPUT_BOX_ADDRESS=%v", config.GetContractsInputBoxAddress())) + s.Env = append(s.Env, + fmt.Sprintf("RD_EPOCH_DURATION=%v", int(config.GetEpochDuration().Seconds()))) + s.Env = append(s.Env, + fmt.Sprintf("CHAIN_ID=%v", config.GetBlockchainId())) + s.Env = append(s.Env, + fmt.Sprintf("DISPATCHER_HTTP_SERVER_PORT=%v", dispatcherPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newGraphQLServer() services.Service { + var s services.Service + s.Name = "graphql-server" + s.HealthcheckPort = graphQLHealthcheckPort + s.Path = "cartesi-rollups-graphql-server" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("graphql_server")) + s.Env = append(s.Env, + fmt.Sprintf("POSTGRES_ENDPOINT=%v", config.GetPostgresEndpoint())) + s.Env = append(s.Env, "GRAPHQL_HOST=127.0.0.1") + s.Env = append(s.Env, + fmt.Sprintf("GRAPHQL_PORT=%v", graphQLServerPort)) + s.Env = append(s.Env, + fmt.Sprintf("GRAPHQL_HEALTHCHECK_PORT=%v", graphQLHealthcheckPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newHostRunner() services.Service { + var s services.Service + s.Name = "host-runner" + s.HealthcheckPort = hostRunnerHealthcheckPort + s.Path = "cartesi-rollups-host-runner" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("host_runner")) + s.Env = append(s.Env, "GRPC_SERVER_MANAGER_ADDRESS=127.0.0.1") + s.Env = append(s.Env, + fmt.Sprintf("GRPC_SERVER_MANAGER_PORT=%v", serverManagerPort)) + s.Env = append(s.Env, "HTTP_ROLLUP_SERVER_ADDRESS=127.0.0.1") + s.Env = append(s.Env, + fmt.Sprintf("HTTP_ROLLUP_SERVER_PORT=%v", hostRunnerRollupsPort)) + s.Env = append(s.Env, + fmt.Sprintf("HOST_RUNNER_HEALTHCHECK_PORT=%v", hostRunnerHealthcheckPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newIndexer() services.Service { + var s services.Service + s.Name = "indexer" + s.HealthcheckPort = indexerPort + s.Path = "cartesi-rollups-indexer" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("indexer")) + s.Env = append(s.Env, + fmt.Sprintf("POSTGRES_ENDPOINT=%v", config.GetPostgresEndpoint())) + s.Env = append(s.Env, + fmt.Sprintf("CHAIN_ID=%v", config.GetBlockchainId())) + s.Env = append(s.Env, + fmt.Sprintf("DAPP_CONTRACT_ADDRESS=%v", config.GetContractsDappAddress())) + s.Env = append(s.Env, + fmt.Sprintf("REDIS_ENDPOINT=redis://127.0.0.1:%v", redisPort)) + s.Env = append(s.Env, + fmt.Sprintf("INDEXER_HEALTHCHECK_PORT=%v", indexerPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newInspectServer() services.Service { + var s services.Service + s.Name = "inspect-server" + s.HealthcheckPort = inspectHealthcheckPort + s.Path = "cartesi-rollups-inspect-server" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("inspect_server")) + s.Env = append(s.Env, + fmt.Sprintf("INSPECT_SERVER_ADDRESS=127.0.0.1:%v", inspectServerPort)) + s.Env = append(s.Env, + fmt.Sprintf("SERVER_MANAGER_ADDRESS=127.0.0.1:%v", serverManagerPort)) + s.Env = append(s.Env, + fmt.Sprintf("SESSION_ID=%v", serverManagerSessionId)) + s.Env = append(s.Env, + fmt.Sprintf("INSPECT_SERVER_HEALTHCHECK_PORT=%v", inspectHealthcheckPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} + +func newRedis() services.Service { + var s services.Service + s.Name = "redis" + s.HealthcheckPort = redisPort + s.Path = "redis-server" + s.Args = append(s.Args, "--port", fmt.Sprint(redisPort)) + return s +} + +func newServerManager() services.Service { + var s services.Service + s.Name = "server-manager" + s.HealthcheckPort = serverManagerPort + s.Path = "server-manager" + s.Args = append(s.Args, fmt.Sprintf("--manager-address=127.0.0.1:%v", serverManagerPort)) + s.Env = append(s.Env, "REMOTE_CARTESI_MACHINE_LOG_LEVEL=info") + if config.GetLogLevel() == config.LogLevelDebug { + s.Env = append(s.Env, "SERVER_MANAGER_LOG_LEVEL=info") + } else { + s.Env = append(s.Env, "SERVER_MANAGER_LOG_LEVEL=warning") + } + return s +} + +func newStateServer() services.Service { + var s services.Service + s.Name = "state-server" + s.HealthcheckPort = stateServerPort + s.Path = "cartesi-rollups-state-server" + s.Env = append(s.Env, "LOG_ENABLE_TIMESTAMP=false") + s.Env = append(s.Env, "LOG_ENABLE_COLOR=false") + s.Env = append(s.Env, getRustLog("state_server")) + s.Env = append(s.Env, "SF_CONCURRENT_EVENTS_FETCH=1") + s.Env = append(s.Env, + fmt.Sprintf("SF_GENESIS_BLOCK=%v", config.GetBlockchainGenesisBlock())) + s.Env = append(s.Env, + fmt.Sprintf("SF_SAFETY_MARGIN=%v", config.GetBlockchainReadDepth())) + s.Env = append(s.Env, + fmt.Sprintf("BH_WS_ENDPOINT=%v", config.GetBlockchainWsEndpoint())) + s.Env = append(s.Env, + fmt.Sprintf("BH_HTTP_ENDPOINT=%v", config.GetBlockchainHttpEndpoint())) + s.Env = append(s.Env, + fmt.Sprintf("BLOCKCHAIN_BLOCK_TIMEOUT=%v", config.GetBlockchainBlockTimeout())) + s.Env = append(s.Env, + fmt.Sprintf("SS_SERVER_ADDRESS=127.0.0.1:%v", stateServerPort)) + s.Env = append(s.Env, os.Environ()...) + return s +} diff --git a/cmd/cartesi-rollups-node/validator.go b/cmd/cartesi-rollups-node/validator.go deleted file mode 100644 index dad9ad815..000000000 --- a/cmd/cartesi-rollups-node/validator.go +++ /dev/null @@ -1,21 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -package main - -import ( - "github.com/cartesi/rollups-node/internal/node" - "github.com/cartesi/rollups-node/internal/services" - "github.com/spf13/cobra" -) - -var validator = &cobra.Command{ - Use: "validator", - Short: "Starts the node in validator mode", - DisableFlagsInUseLine: true, - Run: runValidatorNode, -} - -func runValidatorNode(cmd *cobra.Command, args []string) { - services.Run(cmd.Context(), node.ValidatorServices) -} diff --git a/internal/config/config.go b/internal/config/config.go index 923fd6f1e..3d69b387d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -51,18 +51,6 @@ var ( // Custom GETs // ------------------------------------------------------------------------------------------------ -func GetServerManagerSessionId() string { - return "default_session_id" -} - -func GetInspectServerEndpoint() string { - port, ok := getInspectServerPort() - if !ok { - fail("invalid port") - } - return fmt.Sprintf("0.0.0.0:%d", port) -} - func GetAuth() Auth { // getting the (optional) account index index, _ := getAuthMnemonicAccountIndex() @@ -102,6 +90,10 @@ var cache struct { values map[string]string } +func init() { + cache.values = make(map[string]string) +} + // Reads the value of an environment variable (loads from a cached value when possible). // It returns the value read and true if the variable was set, // otherwise it returns the empty string and false. @@ -166,6 +158,6 @@ func get[T any](name, defaultValue string, hasDefault bool, parser func(string) } func fail(s string, v ...any) { - fmt.Fprintf(os.Stderr, s, v...) + fmt.Fprintf(os.Stderr, s+"\n", v...) os.Exit(1) } diff --git a/internal/config/generate/Config.toml b/internal/config/generate/Config.toml index a0562de11..b12203675 100644 --- a/internal/config/generate/Config.toml +++ b/internal/config/generate/Config.toml @@ -1,216 +1,130 @@ # (c) Cartesi and individual authors (see AUTHORS) # SPDX-License-Identifier: Apache-2.0 (see LICENSE) -# -# Auth -# - -[AUTH_MNEMONIC] -go-type = "string" -export = false - -[AUTH_MNEMONIC_FILE] -go-type = "string" -export = false - -[AUTH_MNEMONIC_ACCOUNT_INDEX] -default = "0" -go-type = "int" -export = false - -[AUTH_AWS_KMS_KEY_ID] -go-type = "string" -export = false - -[AUTH_AWS_KMS_REGION] -go-type = "string" -export = false - # # Logging # [LOG_LEVEL] -default = "warning" +default = "info" go-type = "LogLevel" -[LOG_ENABLE_TIMESTAMP] +[LOG_TIMESTAMP] default = "false" go-type = "bool" -[LOG_ENABLE_COLOR] -default = "true" -go-type = "bool" - -# -# Broker -# - -[BROKER_ENDPOINT] -default = "redis://redis:6379" -go-type = "string" - -[BROKER_CLUSTER_ENDPOINTS] -go-type = "string" - -[BROKER_CONSUME_TIMEOUT] -go-type = "string" - -[BROKER_BACKOFF_MAX_ELAPSED_DURATION] -go-type = "string" - # -# Postgres +# Feature flags # -[POSTGRES_ENDPOINT] -default = "" -go-type = "string" +[FEATURE_HOST_MODE] +default = "false" +go-type = "bool" -[POSTGRES_CONNECTION_POOL_SIZE] -default = "3" -go-type = "int" +[FEATURE_READER_MODE] +default = "false" +go-type = "bool" # # Rollups # [EPOCH_DURATION] -default = "604800" # 1 week in seconds +default = "86400" # 1 day in seconds go-type = "Duration" # -# Deployment -# - -[DAPP_ADDRESS] -go-type = "string" - -[DAPP_BLOCK_HASH] -go-type = "string" - -[HISTORY_ADDRESS] -go-type = "string" - -[AUTHORITY_ADDRESS] -go-type = "string" - -[INPUT_BOX_ADDRESS] -default = "TODO" -go-type = "string" - -# -# State Fold +# Blockchain # -[STATE_FOLD_SAFETY_MARGIN] -default = "20" +[BLOCKCHAIN_ID] go-type = "int" -[STATE_FOLD_BLOCK_TIMEOUT] -go-type = "Duration" - -[STATE_FOLD_CONCURRENT_EVENTS_FETCH] +[BLOCKCHAIN_HTTP_ENDPOINT] go-type = "string" -[STATE_FOLD_QUERY_LIMIT_ERROR_CODES] +[BLOCKCHAIN_WS_ENDPOINT] go-type = "string" -# -# Blockchain -# - -[CHAIN_ID] -go-type = "int" - -[CHAIN_IS_LEGACY] +[BLOCKCHAIN_IS_LEGACY] default = "false" go-type = "bool" -[BLOCKCHAIN_PROVIDER_ENDPOINT] -go-type = "string" - [BLOCKCHAIN_GENESIS_BLOCK] -default = "0" +default = "1" go-type = "int64" -[BLOCKCHAIN_DEFAULT_CONFIRMATIONS] +[BLOCKCHAIN_READ_DEPTH] default = "10" go-type = "int" +[BLOCKCHAIN_BLOCK_TIMEOUT] +default = "60" +go-type = "int" + # -# Health Check Ports +# Contracts deployment # -[INDEXER_HEALTHCHECK_PORT] -go-type = "int" - -[INSPECT_SERVER_HEALTHCHECK_PORT] -go-type = "int" +[CONTRACTS_DAPP_ADDRESS] +go-type = "string" -[GRAPHQL_HEALTHCHECK_PORT] -go-type = "int" +[CONTRACTS_DAPP_DEPLOYMENT_BLOCK_NUMBER] +go-type = "string" -[ADVANCE_RUNNER_HEALTHCHECK_PORT] -go-type = "int" +[CONTRACTS_HISTORY_ADDRESS] +go-type = "string" -# -# GraphQL -# +[CONTRACTS_AUTHORITY_ADDRESS] +go-type = "string" -[GRAPHQL_PORT] -default = "4000" -go-type = "int" +[CONTRACTS_INPUT_BOX_ADDRESS] +go-type = "string" # -# Authority Claimer +# Snapshot # -[AUTHORITY_CLAIMER_PORT] -go-type = "int" - -[TRANSACTION_MANAGER_DATABASE_PATH] +[SNAPSHOT_DIR] go-type = "string" # -# Inspect Server +# Auth # -[INSPECT_SERVER_PORT] -default = "5005" -go-type = "int" +[AUTH_MNEMONIC] +go-type = "string" export = false -[INSPECT_SERVER_QUEUE_SIZE] +[AUTH_MNEMONIC_FILE] +go-type = "string" +export = false + +[AUTH_MNEMONIC_ACCOUNT_INDEX] +default = "0" go-type = "int" +export = false -[INSPECT_SERVER_CONFIG_PATH] +[AUTH_AWS_KMS_KEY_ID] go-type = "string" +export = false -[SERVER_MANAGER_ENDPOINT] +[AUTH_AWS_KMS_REGION] go-type = "string" +export = false # -# State Server +# Postgres # -[STATE_SERVER_ENDPOINT] -go-type = "string" - -[STATE_SERVER_MAX_DECODING_MESSAGE_SIZE] +[POSTGRES_ENDPOINT] +default = "" go-type = "string" # -# Snapshot +# HTTP server # -[SNAPSHOT_ENABLED] -default = "true" -go-type = "bool" - -[SNAPSHOT_DIR] -go-type = "string" - -[SNAPSHOT_LATEST] -go-type = "string" - +[HTTP_PORT] +default = "8080" +go-type = "int" diff --git a/internal/config/generate/main.go b/internal/config/generate/main.go index 3e7eed3ba..448eaf5d4 100644 --- a/internal/config/generate/main.go +++ b/internal/config/generate/main.go @@ -7,6 +7,7 @@ import ( "fmt" "go/format" "os" + "sort" "strings" "unicode" @@ -92,6 +93,13 @@ func main() { panic(err) } + // creating sorted list to make the generated file deterministic + var keys []string + for key := range config { + keys = append(keys, key) + } + sort.Strings(keys) + // creating the header var builder strings.Builder addLine(&builder, `// Code generated by internal/config/generate.`) @@ -115,8 +123,8 @@ func main() { addLine(&builder, "") // adding the functions - for name, env := range config { - addLine(&builder, env.toFunction(name)) + for _, name := range keys { + addLine(&builder, config[name].toFunction(name)) } // assembling the final string diff --git a/internal/config/get.go b/internal/config/get.go index 6f537ed15..c9272bf62 100644 --- a/internal/config/get.go +++ b/internal/config/get.go @@ -14,126 +14,68 @@ type ( Duration = time.Duration ) -func getAuthMnemonicFile() (string, bool) { - return getOptional("AUTH_MNEMONIC_FILE", "", false, toString) -} - -func GetHistoryAddress() string { return get("HISTORY_ADDRESS", "", false, toString) } - -func getAuthMnemonicAccountIndex() (int, bool) { - return getOptional("AUTH_MNEMONIC_ACCOUNT_INDEX", "0", true, toInt) +func getAuthAwsKmsKeyId() (string, bool) { + return getOptional("AUTH_AWS_KMS_KEY_ID", "", false, toString) } -func GetDappBlockHash() string { return get("DAPP_BLOCK_HASH", "", false, toString) } - -func GetIndexerHealthcheckPort() int { return get("INDEXER_HEALTHCHECK_PORT", "", false, toInt) } - -func GetAuthorityAddress() string { return get("AUTHORITY_ADDRESS", "", false, toString) } - -func GetStateFoldConcurrentEventsFetch() string { - return get("STATE_FOLD_CONCURRENT_EVENTS_FETCH", "", false, toString) +func getAuthAwsKmsRegion() (string, bool) { + return getOptional("AUTH_AWS_KMS_REGION", "", false, toString) } -func GetBrokerClusterEndpoints() string { return get("BROKER_CLUSTER_ENDPOINTS", "", false, toString) } +func getAuthMnemonic() (string, bool) { return getOptional("AUTH_MNEMONIC", "", false, toString) } -func GetAdvanceRunnerHealthcheckPort() int { - return get("ADVANCE_RUNNER_HEALTHCHECK_PORT", "", false, toInt) +func getAuthMnemonicAccountIndex() (int, bool) { + return getOptional("AUTH_MNEMONIC_ACCOUNT_INDEX", "0", true, toInt) } -func GetStateServerMaxDecodingMessageSize() string { - return get("STATE_SERVER_MAX_DECODING_MESSAGE_SIZE", "", false, toString) +func getAuthMnemonicFile() (string, bool) { + return getOptional("AUTH_MNEMONIC_FILE", "", false, toString) } -func GetBlockchainDefaultConfirmations() int { - return get("BLOCKCHAIN_DEFAULT_CONFIRMATIONS", "10", true, toInt) -} +func GetBlockchainBlockTimeout() int { return get("BLOCKCHAIN_BLOCK_TIMEOUT", "60", true, toInt) } -func GetStateFoldSafetyMargin() int { return get("STATE_FOLD_SAFETY_MARGIN", "20", true, toInt) } +func GetBlockchainGenesisBlock() int64 { return get("BLOCKCHAIN_GENESIS_BLOCK", "1", true, toInt64) } -func GetServerManagerEndpoint() string { return get("SERVER_MANAGER_ENDPOINT", "", false, toString) } +func GetBlockchainHttpEndpoint() string { return get("BLOCKCHAIN_HTTP_ENDPOINT", "", false, toString) } -func GetPostgresEndpoint() string { return get("POSTGRES_ENDPOINT", "", true, toString) } +func GetBlockchainId() int { return get("BLOCKCHAIN_ID", "", false, toInt) } -func GetBrokerConsumeTimeout() string { return get("BROKER_CONSUME_TIMEOUT", "", false, toString) } +func GetBlockchainIsLegacy() bool { return get("BLOCKCHAIN_IS_LEGACY", "false", true, toBool) } -func GetSnapshotLatest() string { return get("SNAPSHOT_LATEST", "", false, toString) } +func GetBlockchainReadDepth() int { return get("BLOCKCHAIN_READ_DEPTH", "10", true, toInt) } -func getInspectServerPort() (int, bool) { - return getOptional("INSPECT_SERVER_PORT", "5005", true, toInt) -} +func GetBlockchainWsEndpoint() string { return get("BLOCKCHAIN_WS_ENDPOINT", "", false, toString) } -func GetBrokerBackoffMaxElapsedDuration() string { - return get("BROKER_BACKOFF_MAX_ELAPSED_DURATION", "", false, toString) +func GetContractsAuthorityAddress() string { + return get("CONTRACTS_AUTHORITY_ADDRESS", "", false, toString) } -func GetBlockchainGenesisBlock() int64 { return get("BLOCKCHAIN_GENESIS_BLOCK", "0", true, toInt64) } - -func GetLogEnableColor() bool { return get("LOG_ENABLE_COLOR", "true", true, toBool) } - -func GetEpochDuration() Duration { return get("EPOCH_DURATION", "604800", true, toDuration) } - -func GetSnapshotEnabled() bool { return get("SNAPSHOT_ENABLED", "true", true, toBool) } - -func GetLogEnableTimestamp() bool { return get("LOG_ENABLE_TIMESTAMP", "false", true, toBool) } +func GetContractsDappAddress() string { return get("CONTRACTS_DAPP_ADDRESS", "", false, toString) } -func GetPostgresConnectionPoolSize() int { - return get("POSTGRES_CONNECTION_POOL_SIZE", "3", true, toInt) +func GetContractsDappDeploymentBlockNumber() string { + return get("CONTRACTS_DAPP_DEPLOYMENT_BLOCK_NUMBER", "", false, toString) } -func GetChainId() int { return get("CHAIN_ID", "", false, toInt) } - -func GetSnapshotDir() string { return get("SNAPSHOT_DIR", "", false, toString) } - -func GetInputBoxAddress() string { return get("INPUT_BOX_ADDRESS", "TODO", true, toString) } - -func GetGraphqlHealthcheckPort() int { return get("GRAPHQL_HEALTHCHECK_PORT", "", false, toInt) } - -func GetChainIsLegacy() bool { return get("CHAIN_IS_LEGACY", "false", true, toBool) } - -func GetInspectServerQueueSize() int { return get("INSPECT_SERVER_QUEUE_SIZE", "", false, toInt) } - -func GetInspectServerConfigPath() string { - return get("INSPECT_SERVER_CONFIG_PATH", "", false, toString) +func GetContractsHistoryAddress() string { + return get("CONTRACTS_HISTORY_ADDRESS", "", false, toString) } -func GetStateFoldBlockTimeout() Duration { - return get("STATE_FOLD_BLOCK_TIMEOUT", "", false, toDuration) +func GetContractsInputBoxAddress() string { + return get("CONTRACTS_INPUT_BOX_ADDRESS", "", false, toString) } -func getAuthAwsKmsRegion() (string, bool) { - return getOptional("AUTH_AWS_KMS_REGION", "", false, toString) -} +func GetEpochDuration() Duration { return get("EPOCH_DURATION", "86400", true, toDuration) } -func GetStateServerEndpoint() string { return get("STATE_SERVER_ENDPOINT", "", false, toString) } +func GetFeatureHostMode() bool { return get("FEATURE_HOST_MODE", "false", true, toBool) } -func GetBrokerEndpoint() string { return get("BROKER_ENDPOINT", "redis://redis:6379", true, toString) } +func GetFeatureReaderMode() bool { return get("FEATURE_READER_MODE", "false", true, toBool) } -func GetTransactionManagerDatabasePath() string { - return get("TRANSACTION_MANAGER_DATABASE_PATH", "", false, toString) -} +func GetHttpPort() int { return get("HTTP_PORT", "8080", true, toInt) } -func GetLogLevel() LogLevel { return get("LOG_LEVEL", "warning", true, toLogLevel) } +func GetLogLevel() LogLevel { return get("LOG_LEVEL", "info", true, toLogLevel) } -func GetBlockchainProviderEndpoint() string { - return get("BLOCKCHAIN_PROVIDER_ENDPOINT", "", false, toString) -} +func GetLogTimestamp() bool { return get("LOG_TIMESTAMP", "false", true, toBool) } -func getAuthMnemonic() (string, bool) { return getOptional("AUTH_MNEMONIC", "", false, toString) } - -func GetStateFoldQueryLimitErrorCodes() string { - return get("STATE_FOLD_QUERY_LIMIT_ERROR_CODES", "", false, toString) -} - -func GetDappAddress() string { return get("DAPP_ADDRESS", "", false, toString) } - -func GetGraphqlPort() int { return get("GRAPHQL_PORT", "4000", true, toInt) } - -func GetAuthorityClaimerPort() int { return get("AUTHORITY_CLAIMER_PORT", "", false, toInt) } - -func getAuthAwsKmsKeyId() (string, bool) { - return getOptional("AUTH_AWS_KMS_KEY_ID", "", false, toString) -} +func GetPostgresEndpoint() string { return get("POSTGRES_ENDPOINT", "", true, toString) } -func GetInspectServerHealthcheckPort() int { - return get("INSPECT_SERVER_HEALTHCHECK_PORT", "", false, toInt) -} +func GetSnapshotDir() string { return get("SNAPSHOT_DIR", "", false, toString) } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 478965ccd..f5e8d0948 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -6,17 +6,15 @@ // Error and Warning write to [os.Stderr] and Info and Debug write to [os.Stdout]. If Debug is set // as the default level, all logs include file and line number data. // -// The package can be configured with two environment variables: -// -// CARTESI_LOG_LEVEL: defines the main log level. [Info] is the default. -// CARTESI_LOG_ENABLE_TIMESTAMP: a flag that adds date and time information to the log entries. -// It is disabled by default. +// The configuration of the log comes from the config package. package logger import ( "io" "log" "os" + + "github.com/cartesi/rollups-node/internal/config" ) var ( @@ -26,9 +24,9 @@ var ( Debug *log.Logger ) -func Init(logLevel string, enableTimestamp bool) { +func init() { var flags int - if enableTimestamp { + if config.GetLogTimestamp() { flags |= log.Ldate | log.Ltime } @@ -37,16 +35,16 @@ func Init(logLevel string, enableTimestamp bool) { Info = log.New(os.Stdout, "INFO ", flags) Debug = log.New(os.Stdout, "DEBUG ", flags) - switch logLevel { - case "error": + switch config.GetLogLevel() { + case config.LogLevelError: Warning.SetOutput(io.Discard) fallthrough - case "warning": + case config.LogLevelWarning: Info.SetOutput(io.Discard) fallthrough - case "info": + case config.LogLevelInfo: Debug.SetOutput(io.Discard) - case "debug": + case config.LogLevelDebug: flags |= log.Llongfile Error.SetFlags(flags) Warning.SetFlags(flags) diff --git a/internal/node/node.go b/internal/node/node.go deleted file mode 100644 index 96be00d94..000000000 --- a/internal/node/node.go +++ /dev/null @@ -1,102 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -// Package node defines the individual services internally used to implement -// Rollups Node's features -package node - -import ( - "fmt" - "os" - "strings" - - "github.com/cartesi/rollups-node/internal/services" -) - -const ( - serverManagerPort = "5001" - serverManagerAddress = "0.0.0.0:" + serverManagerPort -) - -var ValidatorServices = []services.Service{ - StateServer, // must start before Dispatcher - AdvanceRunner, - AuthorityClaimer, - Dispatcher, - GraphQLServer, - Indexer, - InspectServer, - ServerManager, -} - -var ( - AdvanceRunner = services.NewService( - "advance-runner", - healthcheckPort("advance-runner"), - "cartesi-rollups-advance-runner", - ) - AuthorityClaimer = services.NewService( - "authority-claimer", - healthcheckPort("authority-claimer"), - "cartesi-rollups-authority-claimer", - ) - Dispatcher = services.NewService( - "dispatcher", - healthcheckPort("dispatcher"), - "cartesi-rollups-dispatcher", - ) - GraphQLServer = services.NewService( - "graphql-server", - healthcheckPort("graphql"), - "cartesi-rollups-graphql-server", - ) - Indexer = services.NewService( - "indexer", - healthcheckPort("indexer"), - "cartesi-rollups-indexer", - ) - InspectServer = services.NewService( - "inspect-server", - healthcheckPort("inspect-server"), - "cartesi-rollups-inspect-server", - ) - StateServer = services.NewService( - "state-server", - stateServerHealthcheckPort(), - "cartesi-rollups-state-server", - ) - ServerManager = services.NewService( - "server-manager", - serverManagerPort, - "server-manager", - "--manager-address="+serverManagerAddress, - ) -) - -func healthcheckPort(serviceName string) string { - env := healthcheckEnv(serviceName) - if port, ok := os.LookupEnv(env); ok { - return port - } - panic(fmt.Sprintf("environment variable %s is empty", env)) -} - -func healthcheckEnv(serviceName string) string { - suffix := "_HEALTHCHECK_PORT" - if serviceName == "dispatcher" || serviceName == "authority-claimer" { - suffix = "_HTTP_SERVER_PORT" - } - normalizedName := strings.Replace(serviceName, "-", "_", -1) - return fmt.Sprintf("%s%s", strings.ToUpper(normalizedName), suffix) -} - -func stateServerHealthcheckPort() string { - env := "SS_SERVER_ADDRESS" - if address, ok := os.LookupEnv(env); ok { - split := strings.Split(address, ":") - if len(split) > 1 { - return split[1] - } - } - panic(fmt.Sprintf("environment variable %s is empty", env)) -} diff --git a/internal/services/service.go b/internal/services/service.go index 821544bb2..ec42212fd 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -9,7 +9,6 @@ import ( "context" "fmt" "net" - "os" "os/exec" "sync" "syscall" @@ -23,32 +22,45 @@ const ( DefaultDialInterval = 100 * time.Millisecond ) -type Service struct { - // name identifies the service - name string - // healthcheckPort is a port used to verify if the service is ready - healthcheckPort string - // path to the service binary - path string - // args to the service binary - args []string +type serviceLogger struct { + Name string +} + +func (l serviceLogger) Write(data []byte) (int, error) { + logger.Info.Printf("%v: %v", l.Name, string(data)) + return len(data), nil } -func NewService(name, healthcheckPort, path string, args ...string) Service { - return Service{name, healthcheckPort, path, args} +type Service struct { + + // Name that identifies the service. + Name string + + // Port used to verify if the service is ready. + HealthcheckPort int + + // Path to the service binary. + Path string + + // Args to the service binary. + Args []string + + // Environment variables. + Env []string } // Start will execute a binary and wait for its completion or until the context // is canceled func (s Service) Start(ctx context.Context) error { - cmd := exec.CommandContext(ctx, s.path, s.args...) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout + cmd := exec.CommandContext(ctx, s.Path, s.Args...) + cmd.Env = s.Env + cmd.Stderr = serviceLogger{s.Name} + cmd.Stdout = serviceLogger{s.Name} cmd.Cancel = func() error { err := cmd.Process.Signal(syscall.SIGTERM) if err != nil { msg := "failed to send SIGTERM to %v: %v\n" - logger.Warning.Printf(msg, s.name, err) + logger.Warning.Printf(msg, s.Name, err) } return err } @@ -72,9 +84,9 @@ func (s Service) Ready(ctx context.Context, timeout time.Duration) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() for { - conn, err := net.Dial("tcp", fmt.Sprintf("0.0.0.0:%s", s.healthcheckPort)) + conn, err := net.Dial("tcp", fmt.Sprintf("0.0.0.0:%v", s.HealthcheckPort)) if err == nil { - logger.Debug.Printf("%s is ready\n", s.name) + logger.Debug.Printf("%s is ready\n", s.Name) conn.Close() return nil } @@ -87,7 +99,7 @@ func (s Service) Ready(ctx context.Context, timeout time.Duration) error { } func (s Service) String() string { - return s.name + return s.Name } // The Run function serves as a very simple supervisor: it will start all the @@ -122,7 +134,7 @@ func Run(ctx context.Context, services []Service) { if err := service.Ready(ctx, DefaultServiceTimeout); err != nil { cancel() msg := "main: service '%v' failed to be ready with error: %v. Exiting\n" - logger.Error.Printf(msg, service.name, err) + logger.Error.Printf(msg, service.Name, err) break } } diff --git a/internal/services/service_test.go b/internal/services/service_test.go index 5d0531cf4..9c27d8afb 100644 --- a/internal/services/service_test.go +++ b/internal/services/service_test.go @@ -12,7 +12,6 @@ import ( "testing" "time" - "github.com/cartesi/rollups-node/internal/logger" "github.com/stretchr/testify/suite" ) @@ -23,7 +22,6 @@ type ServiceTestSuite struct { } func (s *ServiceTestSuite) SetupSuite() { - logger.Init("warning", false) s.buildFakeService() s.servicePort = 55555 } @@ -44,9 +42,9 @@ func (s *ServiceTestSuite) SetupTest() { // Service should stop when context is cancelled func (s *ServiceTestSuite) TestServiceStops() { service := Service{ - name: "fake-service", - path: "fake-service", - healthcheckPort: fmt.Sprint(s.servicePort), + Name: "fake-service", + Path: "fake-service", + HealthcheckPort: s.servicePort, } ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -68,9 +66,9 @@ func (s *ServiceTestSuite) TestServiceStops() { // Service should stop if timeout is reached and it isn't ready yet func (s *ServiceTestSuite) TestServiceTimeout() { service := Service{ - name: "fake-service", - path: "fake-service", - healthcheckPort: "0000", // wrong port + Name: "fake-service", + Path: "fake-service", + HealthcheckPort: 0, // wrong port } ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -93,9 +91,9 @@ func (s *ServiceTestSuite) TestServiceTimeout() { // Service should be ready soon after starting func (s *ServiceTestSuite) TestServiceReady() { service := Service{ - name: "fake-service", - path: "fake-service", - healthcheckPort: fmt.Sprint(s.servicePort), + Name: "fake-service", + Path: "fake-service", + HealthcheckPort: s.servicePort, } ctx, cancel := context.WithCancel(context.Background()) defer cancel()