Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7a3951d
fix(kms): make the AWS KMS signer support dynamic-fee transactions
renatomaia Jul 21, 2026
5b17f6d
test(kms): add unit test for AWS KMS authentication
renatomaia Aug 25, 2026
7e388a3
test(integration): add make target to run AWS LocalStack
renatomaia Jul 22, 2026
ad56d20
test(integration): fix AWS KMS subtest execution
renatomaia Aug 27, 2026
4b17c3d
test(integration): use new style for Ethereum transactions for better…
renatomaia Aug 27, 2026
4445d6c
test(integration): only run AWS Local Stack for required tests
renatomaia Aug 27, 2026
e5bcf39
test(integration): fix clean up of configurations used in tests
renatomaia Aug 27, 2026
d6579d7
test(integration): isolate AWS KMS funding account
renatomaia Aug 27, 2026
c302b18
test(integration): allow built-in readness check of AWS LocalStack image
renatomaia Aug 27, 2026
cd3705e
test(integration): clean up AWS KMS test resources
renatomaia Aug 27, 2026
71fa8fe
fix(kms): remove redundant configuration variables for AWS KMS
renatomaia Aug 25, 2026
a989251
fix(kms): handle invalid AWS KMS authentication
renatomaia Aug 26, 2026
8658356
test(kms): improve test coverage of AWS KMS authentication
renatomaia Aug 26, 2026
67da6ca
refactor(kms): fix small details in implementation and tests
renatomaia Aug 26, 2026
7db8476
style(kms): avoid lint errors
renatomaia Aug 26, 2026
2eda6ed
feat(claimer,prt): log submitter identity on service startup
renatomaia Aug 24, 2026
05ee583
fix(cli): use a single authentication for all deposit transactions
renatomaia Aug 26, 2026
275baf3
feat(node): delay startup of services until able to authenticate
renatomaia Aug 26, 2026
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
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
version: "2"
run:
build-tags:
- endtoendtests
linters:
enable:
- exhaustive
Expand Down
26 changes: 23 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,17 @@ start-postgres: ## Run the PostgreSQL 16 docker container
@docker run --rm --name postgres -p 5432:5432 -d -e POSTGRES_PASSWORD=password -e POSTGRES_DB=rollupsdb -v $(CURDIR)/test/postgres/init-test-db.sh:/docker-entrypoint-initdb.d/init-test-db.sh postgres:18-alpine
@$(MAKE) migrate

start-awslocalstack: ## Run the AWS LocalStack docker container
@echo "Starting AWS localstack"
@docker run --rm --name awslocalstack -p 127.0.0.1:4566:4566 -d -e SERVICES=kms localstack/localstack:4.14.0
@echo "Add the following variables to run integration test with AWS services:"
@echo " export AWS_ACCESS_KEY_ID=test"
@echo " export AWS_SECRET_ACCESS_KEY=test"
@echo " export AWS_REGION=us-east-1"
@echo " export AWS_ENDPOINT_URL_KMS=http://localhost:4566"
@echo " export LOCALSTACK_KMS_ENDPOINT=http://localhost:4566"
@echo " export LOCALSTACK_KMS_REQUIRED=true"

start: start-postgres start-devnet ## Start the anvil devnet and PostgreSQL 16 docker containers

stop-devnet: ## Stop the anvil devnet docker container
Expand All @@ -524,7 +535,10 @@ stop-devnet: ## Stop the anvil devnet docker container
stop-postgres: ## Stop the PostgreSQL 16 docker container
@docker stop postgres || true

stop: stop-devnet stop-postgres ## Stop all running docker containers
stop-awslocalstack: ## Stop the AWS LocalStack docker container
@docker stop awslocalstack || true

stop: stop-devnet stop-postgres ## Stop the anvil devnet and PostgreSQL 16 docker containers

restart-devnet: ## Restart the anvil devnet docker container
@$(MAKE) stop-devnet
Expand Down Expand Up @@ -571,14 +585,15 @@ check-license: ## Verify license headers on Go source files
# Discovery (integration-test-shard-check) lists tests with a plain Go
# toolchain, so the integration package must stay free of the Cartesi CGo
# dependency for the check to build on the CI setup runner.
INTEGRATION_SHARDS := basic quorum prt replay restart withdrawal
INTEGRATION_SHARDS := basic quorum prt replay restart withdrawal awskms

INTEGRATION_SHARD_basic := ^Test(EchoAuthority|RejectException|MultiApp|EchoAuthorityStaging)$$
INTEGRATION_SHARD_quorum := ^Test(EchoQuorum|SameBlockInputs)$$
INTEGRATION_SHARD_prt := ^Test(EchoPrt|RejectExceptionPrt|ForeclosePrt)$$
INTEGRATION_SHARD_replay := ^Test(Foreclose|ForecloseReplay|DivergentClaim)$$
INTEGRATION_SHARD_restart := ^Test(Restart|SnapshotPolicy)$$
INTEGRATION_SHARD_withdrawal := ^TestWithdrawalLifecycle$$
INTEGRATION_SHARD_awskms := ^TestLocalStackAWSIntegration$$

# -----------------------------------------------------------------------------
# Node topology axis — orthogonal to shards.
Expand All @@ -600,7 +615,7 @@ INTEGRATION_TOPOLOGIES := standalone multiprocess
NODE_TOPOLOGY ?= standalone

INTEGRATION_SHARDS_standalone := $(INTEGRATION_SHARDS)
INTEGRATION_SHARDS_multiprocess := $(INTEGRATION_SHARDS)
INTEGRATION_SHARDS_multiprocess := $(filter-out awskms,$(INTEGRATION_SHARDS))

# The CI matrix is the set of (shard, topology) cells, encoded "shard:topology".
INTEGRATION_CELLS := $(foreach t,$(INTEGRATION_TOPOLOGIES),$(foreach s,$(INTEGRATION_SHARDS_$(t)),$(s):$(t)))
Expand All @@ -624,6 +639,9 @@ TOPOLOGIES_SELECTED = $(if $(filter all,$(NODE_TOPOLOGY)),$(INTEGRATION_TOPOLOGI
shards_for = $(filter $(if $(strip $(SHARD)),$(SHARD),$(INTEGRATION_SHARDS_$(1))),$(INTEGRATION_SHARDS_$(1)))
# run_pattern(topology): the selected shards' -run regexes as one alternation.
run_pattern = $(subst $(space),|,$(strip $(foreach s,$(call shards_for,$(1)),$(INTEGRATION_SHARD_$(s)))))
# compose_profiles(topology): activate optional infrastructure required by the
# selected shards for this topology.
compose_profiles = $(if $(filter awskms,$(call shards_for,$(1))),awskms,)
# Selected (shard:topology) cells, for PARALLEL fan-out.
SELECTED_CELLS = $(foreach t,$(TOPOLOGIES_SELECTED),$(foreach s,$(call shards_for,$(t)),$(s):$(t)))
# Label for project/log names: the SHARD filter joined by '-', or "all".
Expand Down Expand Up @@ -671,6 +689,7 @@ _compose-topology-%:
COMPOSE_PROJECT='$(if $(filter rollups-node-integration,$(COMPOSE_PROJECT)),rollups-node-integration-$(SUITE_LABEL)-$*,$(COMPOSE_PROJECT))' \
INTEGRATION_LOGS='integration-logs-$(SUITE_LABEL)-$*.txt' \
TEST_PATTERN="$$pattern" SHARD_NAME='$(SUITE_LABEL)-$*' NODE_TOPOLOGY='$*' \
COMPOSE_PROFILES='$(call compose_profiles,$*)' \
GOTESTSUM_FORMAT='$(COMPOSE_TOPOLOGY_GOTESTSUM_FORMAT)' \
scripts/compose-integration-run.sh

Expand All @@ -681,6 +700,7 @@ _compose-cell-%:
TEST_PATTERN='$(INTEGRATION_SHARD_$(firstword $(subst :, ,$*)))' \
SHARD_NAME='$(firstword $(subst :, ,$*))' \
NODE_TOPOLOGY='$(lastword $(subst :, ,$*))' \
COMPOSE_PROFILES='$(if $(filter awskms,$(firstword $(subst :, ,$*))),awskms,)' \
GOTESTSUM_FORMAT='$(GOTESTSUM_FORMAT)' \
scripts/compose-integration-run.sh

Expand Down
9 changes: 5 additions & 4 deletions cmd/cartesi-rollups-cli/root/deposit/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/cartesi/rollups-node/cmd/cartesi-rollups-cli/util"
"github.com/cartesi/rollups-node/internal/cli"
"github.com/cartesi/rollups-node/internal/config"
"github.com/cartesi/rollups-node/internal/config/auth"
"github.com/cartesi/rollups-node/pkg/contracts/iapplication"
"github.com/cartesi/rollups-node/pkg/contracts/ierc20errors"
"github.com/cartesi/rollups-node/pkg/contracts/ierc20metadata"
Expand Down Expand Up @@ -113,7 +114,7 @@ func runERC20(cmd *cobra.Command, args []string) {
cobra.CheckErr(err)
chainID, err := client.ChainID(ctx)
cobra.CheckErr(err)
txOpts, err := cli.GetTransactOpts(ctx, chainID)
txOptsFactory, err := auth.GetTransactOptsFactory(ctx, chainID)
cobra.CheckErr(err)

if !skipConfirmation {
Expand All @@ -124,7 +125,7 @@ func runERC20(cmd *cobra.Command, args []string) {
" token: %s\n"+
" amount: %s\n"+
" approve: %t\n",
txOpts.From, appAddr, portalAddr, tokenAddr, amount.String(), approveParam)
txOptsFactory.From(), appAddr, portalAddr, tokenAddr, amount.String(), approveParam)
confirmed, promptErr := cli.ConfirmPrompt("Do you want to continue?")
cobra.CheckErr(promptErr)
if !confirmed {
Expand All @@ -137,7 +138,7 @@ func runERC20(cmd *cobra.Command, args []string) {
if approveParam {
token, err := ierc20metadata.NewIERC20Metadata(tokenAddr, client)
cobra.CheckErr(err)
approveOpts, err := cli.GetTransactOpts(ctx, chainID)
approveOpts, err := cli.GetTransactOptsFromFactory(ctx, txOptsFactory)
cobra.CheckErr(err)
tx, err := token.Approve(approveOpts, portalAddr, amount)
cobra.CheckErr(cli.DecorateRevert(err,
Expand All @@ -153,7 +154,7 @@ func runERC20(cmd *cobra.Command, args []string) {

portal, err := ierc20portal.NewIERC20Portal(portalAddr, client)
cobra.CheckErr(err)
depositOpts, err := cli.GetTransactOpts(ctx, chainID)
depositOpts, err := cli.GetTransactOptsFromFactory(ctx, txOptsFactory)
cobra.CheckErr(err)
tx, err := portal.DepositERC20Tokens(depositOpts, tokenAddr, appAddr, amount, execData)
// The revert can come from three layers: the portal itself
Expand Down
9 changes: 7 additions & 2 deletions internal/claimer/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ type PersistentConfig struct {
ChainID uint64
}

func Create(ctx context.Context, c *CreateInfo) (*Service, error) {
var err error
func Create(ctx context.Context, c *CreateInfo) (_ *Service, err error) {

if c == nil {
return nil, errors.New("invalid CreateInfo is nil")
Expand All @@ -101,6 +100,11 @@ func Create(ctx context.Context, c *CreateInfo) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("creating base service: %w", err)
}
defer func() {
if err != nil && s.Ticker != nil {
s.Ticker.Stop()
}
}()

nodeConfig, err := setupPersistentConfig(ctx, s.Logger, c.Repository, &c.Config)
if err != nil {
Expand Down Expand Up @@ -138,6 +142,7 @@ func Create(ctx context.Context, c *CreateInfo) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("getting transaction options: %w", err)
}
s.Logger.Info("Claim submitter identity", "address", txOptsFactory.From())
}

s.repository = c.Repository
Expand Down
7 changes: 7 additions & 0 deletions internal/cli/ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/cartesi/rollups-node/internal/config"
"github.com/cartesi/rollups-node/internal/config/auth"
"github.com/cartesi/rollups-node/pkg/ethutil"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)

Expand All @@ -18,7 +19,13 @@ func GetTransactOpts(ctx context.Context, chainId *big.Int) (*bind.TransactOpts,
if err != nil {
return nil, err
}
return GetTransactOptsFromFactory(ctx, factory)
}

func GetTransactOptsFromFactory(
ctx context.Context,
factory ethutil.TransactOptsFactory,
) (*bind.TransactOpts, error) {
txOpts, err := factory.NewTransactOpts(ctx)
if err != nil {
return nil, err
Expand Down
29 changes: 22 additions & 7 deletions internal/config/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package auth

import (
"context"
"errors"
"fmt"
"math/big"

Expand All @@ -21,7 +22,17 @@ import (
"github.com/cartesi/rollups-node/pkg/ethutil"
)

// ErrSignerUnavailable identifies transient failures while acquiring the AWS
// KMS-backed signer. Callers may use this to degrade and retry signing services
// without treating unrelated configuration or service creation errors as
// recoverable.
var ErrSignerUnavailable = errors.New("signer unavailable")

func GetTransactOptsFactory(ctx context.Context, chainId *big.Int) (ethutil.TransactOptsFactory, error) {
if chainId == nil || chainId.Sign() <= 0 {
return nil, bind.ErrNoChainID
}

authKind, err := GetAuthKind()
if err != nil {
return nil, err
Expand Down Expand Up @@ -60,21 +71,25 @@ func GetTransactOptsFactory(ctx context.Context, chainId *big.Int) (ethutil.Tran
}
return ethutil.NewStaticTransactOptsFactory(txOpts), nil
case AuthKindAWS:
awsc, err := aws_cfg.LoadDefaultConfig(ctx)
keyId, err := GetAuthAwsKmsKeyId()
if err != nil {
return nil, err
}
kmsConfig := aws_kms.NewFromConfig(awsc)
authAwsKmsKeyId, err := GetAuthAwsKmsKeyId()
awsCfg, err := aws_cfg.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
return signtx.CreateAWSTransactOptsFactory(
kmsClient := aws_kms.NewFromConfig(awsCfg)
factory, err := signtx.CreateAWSTransactOptsFactory(
ctx,
kmsConfig,
aws.String(authAwsKmsKeyId.Value),
types.NewEIP155Signer(chainId),
kmsClient,
aws.String(keyId.Value),
types.LatestSignerForChainID(chainId),
)
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrSignerUnavailable, err)
}
return factory, nil
default:
return nil, fmt.Errorf("no valid authentication method found")
}
Expand Down
Loading