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
22 changes: 20 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ ifneq ($(TEST_PACKAGES),)
GO_TEST_PACKAGES := $(addprefix ./, $(addsuffix /..., $(subst :, ,$(TEST_PACKAGES))))
endif

COVERAGE_DIR := coverage
COVER ?=
ifeq ($(COVER),true)
GO_TEST_FLAGS += -coverprofile=$(COVERAGE_DIR)/coverage.out -covermode=atomic -coverpkg=./...
COVER_DEPS := $(COVERAGE_DIR)
COVER_REPORT := coverage-report
endif


ROLLUPS_CONTRACTS_ABI_BASEDIR:= rollups-contracts/
ROLLUPS_PRT_CONTRACTS_ABI_BASEDIR:= rollups-prt-contracts/
Expand Down Expand Up @@ -190,6 +198,7 @@ clean-go: ## Clean Go artifacts
@echo "Cleaning Go artifacts"
@go clean -i -r -cache
@rm -f $(GO_ARTIFACTS)
@rm -rf $(COVERAGE_DIR)

clean-contracts: ## Clean contract artifacts
@echo "Cleaning contract artifacts"
Expand Down Expand Up @@ -221,10 +230,19 @@ clean-test-dependencies: ## Clean the test dependencies
# =============================================================================
test: unit-test ## Execute all tests

unit-test: ## Execute go unit tests
$(COVERAGE_DIR):
@mkdir -p $@

coverage-report:
@go tool cover -func=$(COVERAGE_DIR)/coverage.out
@go tool cover -html=$(COVERAGE_DIR)/coverage.out -o $(COVERAGE_DIR)/coverage.html
@echo "Coverage report: $(COVERAGE_DIR)/coverage.html"

unit-test: $(COVER_DEPS) ## Execute go unit tests
@echo "Running go unit tests"
@go clean -testcache
@go test -p 1 $(GO_BUILD_PARAMS) $(GO_TEST_FLAGS) $(GO_TEST_PACKAGES)
@$(if $(COVER_REPORT),$(MAKE) $(COVER_REPORT))

integration-test: ## Execute e2e tests
@echo "Running end-to-end tests"
Expand Down Expand Up @@ -417,4 +435,4 @@ build-debian-package: install
sed 's|ARG_VERSION|$(ROLLUPS_NODE_VERSION)|g;s|ARG_ARCH|$(DEB_ARCH)|g' control.template > $(DESTDIR)/DEBIAN/control
dpkg-deb -Zxz --root-owner-group --build $(DESTDIR) $(DEB_FILENAME)

.PHONY: build build-go clean clean-go test unit-test-go e2e-test lint fmt vet escape md-lint devnet image run-with-compose shutdown-compose help docs $(GO_ARTIFACTS)
.PHONY: build build-go clean clean-go test unit-test-go e2e-test lint fmt vet escape md-lint devnet image run-with-compose shutdown-compose help docs coverage-report $(GO_ARTIFACTS)
6 changes: 3 additions & 3 deletions internal/claimer/claimer.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ type iclaimerRepository interface {

UpdateEpochWithSubmittedClaim(
ctx context.Context,
application_id int64,
applicationID int64,
index uint64,
transaction_hash common.Hash,
transactionHash common.Hash,
) error

UpdateEpochWithAcceptedClaim(
ctx context.Context,
application_id int64,
applicationID int64,
index uint64,
) error

Expand Down
123 changes: 63 additions & 60 deletions internal/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package node
import (
"context"
"fmt"
"os"

"github.com/cartesi/rollups-node/pkg/service"

Expand All @@ -22,6 +21,13 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
)

// serviceResult carries either a successfully created service or an error
// back from the goroutines in createServices.
type serviceResult struct {
service service.IService
err error
}

type CreateInfo struct {
service.CreateInfo

Expand Down Expand Up @@ -66,56 +72,59 @@ func Create(ctx context.Context, c *CreateInfo) (*Service, error) {
return s, nil
}

type serviceCreator func(context.Context, *CreateInfo, *Service) (service.IService, error)

func createServices(ctx context.Context, c *CreateInfo, s *Service) error {
// Count services first
numChildren := 5 // evm-reader, advancer, validator, claimer, prt
creators := []serviceCreator{
newEVMReader,
newAdvancer,
newValidator,
newClaimer,
newPrt,
}
if c.Config.FeatureJsonrpcApiEnabled {
numChildren++ // jsonrpc
creators = append(creators, newJsonrpc)
}

// Create buffered channel with correct size
ch := make(chan service.IService, numChildren)

go func() {
ch <- newEVMReader(ctx, c, s)
}()

go func() {
ch <- newAdvancer(ctx, c, s)
}()

go func() {
ch <- newValidator(ctx, c, s)
}()

go func() {
ch <- newClaimer(ctx, c, s)
}()

go func() {
ch <- newPrt(ctx, c, s)
}()

if c.Config.FeatureJsonrpcApiEnabled {
ch := make(chan serviceResult, len(creators))
for _, create := range creators {
go func() {
ch <- newJsonrpc(ctx, c, s)
svc, err := create(ctx, c, s)
ch <- serviceResult{service: svc, err: err}
}()
}

for range numChildren {
for range len(creators) {
select {
case child := <-ch:
s.Children = append(s.Children, child)
case result := <-ch:
if result.err != nil {
stopAndDrain(s.Children, ch, len(creators)-len(s.Children)-1)
return fmt.Errorf("failed to create service: %w", result.err)
}
s.Children = append(s.Children, result.service)
case <-ctx.Done():
err := ctx.Err()
s.Logger.Error("Failed to create services. Time limit exceeded",
"err", err)
return fmt.Errorf("failed to create services. Time limit exceeded")
stopAndDrain(s.Children, ch, len(creators)-len(s.Children))
return fmt.Errorf("failed to create services: %w", ctx.Err())
}
}
return nil
}

// stopAndDrain stops already-created children and drains remaining results
// from the channel, stopping any successful services to prevent resource leaks.
func stopAndDrain(children []service.IService, ch <-chan serviceResult, remaining int) {
for _, child := range children {
child.Stop(true)
}
go func() {
for range remaining {
if r := <-ch; r.err == nil && r.service != nil {
r.service.Stop(true)
}
}
}()
}

func (me *Service) Alive() bool {
allAlive := true
for _, s := range me.Children {
Expand Down Expand Up @@ -151,7 +160,7 @@ func (me *Service) Serve() error {

// services creation

func newEVMReader(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newEVMReader(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
readerArgs := evmreader.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "evm-reader",
Expand All @@ -171,13 +180,12 @@ func newEVMReader(ctx context.Context, c *CreateInfo, s *Service) service.IServi

readerService, err := evmreader.Create(ctx, &readerArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create evm-reader: %w", err)
}
return readerService
return readerService, nil
}

func newAdvancer(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newAdvancer(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
advancerArgs := advancer.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "advancer",
Expand All @@ -196,13 +204,12 @@ func newAdvancer(ctx context.Context, c *CreateInfo, s *Service) service.IServic

advancerService, err := advancer.Create(ctx, &advancerArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create advancer: %w", err)
}
return advancerService
return advancerService, nil
}

func newValidator(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newValidator(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
validatorArgs := validator.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "validator",
Expand All @@ -221,13 +228,12 @@ func newValidator(ctx context.Context, c *CreateInfo, s *Service) service.IServi

validatorService, err := validator.Create(ctx, &validatorArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create validator: %w", err)
}
return validatorService
return validatorService, nil
}

func newClaimer(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newClaimer(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
claimerArgs := claimer.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "claimer",
Expand All @@ -247,13 +253,12 @@ func newClaimer(ctx context.Context, c *CreateInfo, s *Service) service.IService

claimerService, err := claimer.Create(ctx, &claimerArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create claimer: %w", err)
}
return claimerService
return claimerService, nil
}

func newJsonrpc(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newJsonrpc(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
jsonrpcArgs := jsonrpc.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "jsonrpc",
Expand All @@ -271,13 +276,12 @@ func newJsonrpc(ctx context.Context, c *CreateInfo, s *Service) service.IService

jsonrpcService, err := jsonrpc.Create(ctx, &jsonrpcArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create jsonrpc: %w", err)
}
return jsonrpcService
return jsonrpcService, nil
}

func newPrt(ctx context.Context, c *CreateInfo, s *Service) service.IService {
func newPrt(ctx context.Context, c *CreateInfo, s *Service) (service.IService, error) {
prtArgs := prt.CreateInfo{
CreateInfo: service.CreateInfo{
Name: "prt",
Expand All @@ -297,8 +301,7 @@ func newPrt(ctx context.Context, c *CreateInfo, s *Service) service.IService {

prtService, err := prt.Create(ctx, &prtArgs)
if err != nil {
s.Logger.Error("Fatal", "error", err)
os.Exit(1)
return nil, fmt.Errorf("create prt: %w", err)
}
return prtService
return prtService, nil
}
4 changes: 3 additions & 1 deletion internal/repository/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ func NewRepositoryFromConnectionString(ctx context.Context, conn string) (Reposi
// case strings.HasPrefix(lowerConn, "sqlite://"):
// return newSQLiteRepository(ctx, conn)
default:
return nil, fmt.Errorf("unrecognized connection string format: %s", conn)
return nil, fmt.Errorf(
"unrecognized connection string scheme (expected postgres:// or postgresql://)",
)
}
}

Expand Down
Loading
Loading