Skip to content

Commit e412f81

Browse files
committed
fix(kms): make the AWS KMS signer support dynamic-fee transactions
1 parent deb34e0 commit e412f81

5 files changed

Lines changed: 138 additions & 2 deletions

File tree

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,14 +553,15 @@ check-license: ## Verify license headers on Go source files
553553
# Discovery (integration-test-shard-check) lists tests with a plain Go
554554
# toolchain, so the integration package must stay free of the Cartesi CGo
555555
# dependency for the check to build on the CI setup runner.
556-
INTEGRATION_SHARDS := basic quorum prt replay restart withdrawal
556+
INTEGRATION_SHARDS := basic quorum prt replay restart withdrawal awskms
557557

558558
INTEGRATION_SHARD_basic := ^Test(EchoAuthority|RejectException|MultiApp|EchoAuthorityStaging)$$
559559
INTEGRATION_SHARD_quorum := ^Test(EchoQuorum|SameBlockInputs)$$
560560
INTEGRATION_SHARD_prt := ^Test(EchoPrt|RejectExceptionPrt|ForeclosePrt)$$
561561
INTEGRATION_SHARD_replay := ^Test(Foreclose|ForecloseReplay|DivergentClaim)$$
562562
INTEGRATION_SHARD_restart := ^Test(Restart|SnapshotPolicy)$$
563563
INTEGRATION_SHARD_withdrawal := ^TestWithdrawalLifecycle$$
564+
INTEGRATION_SHARD_awskms := ^TestLocalStackAWSTransactOptsFactory$$
564565

565566
# -----------------------------------------------------------------------------
566567
# Node topology axis — orthogonal to shards.

internal/config/auth/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func GetTransactOptsFactory(ctx context.Context, chainId *big.Int) (ethutil.Tran
7373
ctx,
7474
kmsConfig,
7575
aws.String(authAwsKmsKeyId.Value),
76-
types.NewEIP155Signer(chainId),
76+
types.LatestSignerForChainID(chainId),
7777
)
7878
default:
7979
return nil, fmt.Errorf("no valid authentication method found")

internal/kms/signtx_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,37 @@ func TestAWSTransactOptsFactorySignsWithSubmitContext(t *testing.T) {
128128
require.NoError(t, client.signContext.Err())
129129
}
130130

131+
func TestAWSTransactOptsFactorySignsDynamicFeeTransaction(t *testing.T) {
132+
privateKey, err := crypto.GenerateKey()
133+
require.NoError(t, err)
134+
135+
chainID := big.NewInt(31337)
136+
client := newFakeKMSClient(t, privateKey)
137+
keyID := "alias/test-key"
138+
factory, err := CreateAWSTransactOptsFactory(
139+
context.Background(), client, &keyID, ethtypes.LatestSignerForChainID(chainID),
140+
)
141+
require.NoError(t, err)
142+
143+
opts, err := factory.NewTransactOpts(context.Background())
144+
require.NoError(t, err)
145+
tx := ethtypes.NewTx(&ethtypes.DynamicFeeTx{
146+
ChainID: chainID,
147+
Nonce: 1,
148+
GasTipCap: big.NewInt(1),
149+
GasFeeCap: big.NewInt(2),
150+
Gas: 21000,
151+
To: &common.Address{0x01},
152+
Value: big.NewInt(3),
153+
})
154+
signed, err := opts.Signer(opts.From, tx)
155+
require.NoError(t, err)
156+
157+
sender, err := ethtypes.Sender(ethtypes.LatestSignerForChainID(chainID), signed)
158+
require.NoError(t, err)
159+
require.Equal(t, crypto.PubkeyToAddress(privateKey.PublicKey), sender)
160+
}
161+
131162
type fakeKMSClient struct {
132163
t *testing.T
133164
privateKey *ecdsa.PrivateKey

test/compose/compose.integration.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ services:
8383
<<: *env
8484
restart: "no"
8585

86+
localstack:
87+
image: localstack/localstack:4.14.0
88+
networks:
89+
- devnet
90+
healthcheck:
91+
test: ["CMD", "curl", "-fsS", "http://localhost:4566/_localstack/health"]
92+
interval: 2s
93+
timeout: 2s
94+
retries: 30
95+
environment:
96+
SERVICES: kms
97+
8698
# The node is started and managed by TestMain inside the test process.
8799
# This ensures all tests (including restart and snapshot policy tests)
88100
# run with the same infrastructure in both local and CI environments.
@@ -99,6 +111,8 @@ services:
99111
condition: service_healthy
100112
dapp-builder:
101113
condition: service_completed_successfully
114+
localstack:
115+
condition: service_healthy
102116
volumes:
103117
- dapp_images:/var/lib/cartesi-rollups-node/dapps:ro
104118
- node_logs:/var/lib/cartesi-rollups-node/logs
@@ -125,6 +139,12 @@ services:
125139
CARTESI_TEST_ERC20_WITHDRAWAL_DAPP_PATH: /var/lib/cartesi-rollups-node/dapps/erc20-withdrawal-dapp
126140
CARTESI_TEST_NODE_LOG_FILE: /var/lib/cartesi-rollups-node/logs/node.log
127141
CARTESI_INSPECT_URL: http://localhost:10012/
142+
# test/integration/localstack_integration_test.go
143+
AWS_ACCESS_KEY_ID: test
144+
AWS_SECRET_ACCESS_KEY: test
145+
AWS_REGION: us-east-1
146+
LOCALSTACK_KMS_ENDPOINT: http://localstack:4566
147+
LOCALSTACK_KMS_REQUIRED: "true"
128148

129149
volumes:
130150
dapp_images:
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
//go:build endtoendtests
5+
6+
package integration
7+
8+
import (
9+
"context"
10+
"math/big"
11+
"os"
12+
"testing"
13+
14+
"github.com/cartesi/rollups-node/internal/kms"
15+
16+
"github.com/aws/aws-sdk-go-v2/aws"
17+
"github.com/aws/aws-sdk-go-v2/config"
18+
awskms "github.com/aws/aws-sdk-go-v2/service/kms"
19+
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
20+
"github.com/ethereum/go-ethereum/common"
21+
"github.com/ethereum/go-ethereum/core/types"
22+
"github.com/stretchr/testify/require"
23+
)
24+
25+
func TestLocalStackAWSTransactOptsFactory(t *testing.T) {
26+
endpoint := os.Getenv("LOCALSTACK_KMS_ENDPOINT")
27+
if endpoint == "" {
28+
t.Skip("LOCALSTACK_KMS_ENDPOINT is not set; skipping LocalStack KMS integration test")
29+
}
30+
ctx := context.Background()
31+
cfg, err := config.LoadDefaultConfig(ctx,
32+
config.WithRegion("us-east-1"),
33+
config.WithBaseEndpoint(endpoint),
34+
)
35+
require.NoError(t, err)
36+
client := awskms.NewFromConfig(cfg)
37+
38+
created, err := client.CreateKey(ctx, &awskms.CreateKeyInput{
39+
KeyUsage: kmstypes.KeyUsageTypeSignVerify,
40+
KeySpec: kmstypes.KeySpecEccSecgP256k1,
41+
})
42+
if err != nil {
43+
message := "unable to create key on LocalStack"
44+
if os.Getenv("LOCALSTACK_KMS_REQUIRED") == "true" {
45+
t.Fatalf("%s: %v", message, err)
46+
}
47+
t.Skipf("%s: %v", message, err)
48+
}
49+
require.NotNil(t, created.KeyMetadata)
50+
require.NotNil(t, created.KeyMetadata.KeyId)
51+
52+
chainID := big.NewInt(31337)
53+
factory, err := kms.CreateAWSTransactOptsFactory(
54+
ctx, client, created.KeyMetadata.KeyId, types.LatestSignerForChainID(chainID),
55+
)
56+
require.NoError(t, err)
57+
require.NotEqual(t, common.Address{}, factory.From())
58+
opts, err := factory.NewTransactOpts(ctx)
59+
require.NoError(t, err)
60+
61+
to := common.Address{0x01}
62+
tests := map[string]*types.Transaction{
63+
"legacy": types.NewTx(&types.LegacyTx{
64+
Nonce: 1, GasPrice: big.NewInt(2), Gas: 21000, To: &to, Value: big.NewInt(3),
65+
}),
66+
"dynamic fee": types.NewTx(&types.DynamicFeeTx{
67+
ChainID: chainID, Nonce: 2, GasTipCap: big.NewInt(1), GasFeeCap: big.NewInt(2),
68+
Gas: 21000, To: &to, Value: big.NewInt(3),
69+
}),
70+
}
71+
for name, tx := range tests {
72+
t.Run(name, func(t *testing.T) {
73+
signed, err := opts.Signer(opts.From, tx)
74+
require.NoError(t, err)
75+
sender, err := types.Sender(types.LatestSignerForChainID(chainID), signed)
76+
require.NoError(t, err)
77+
require.Equal(t, factory.From(), sender)
78+
})
79+
}
80+
81+
_, _ = client.ScheduleKeyDeletion(ctx, &awskms.ScheduleKeyDeletionInput{
82+
KeyId: created.KeyMetadata.KeyId, PendingWindowInDays: aws.Int32(7), //nolint:mnd
83+
})
84+
}

0 commit comments

Comments
 (0)