Skip to content

Commit a4290be

Browse files
authored
Aptos network support (#1562)
Aptos network support
1 parent 9658154 commit a4290be

File tree

17 files changed

+356
-77
lines changed

17 files changed

+356
-77
lines changed

.github/workflows/framework-golden-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ jobs:
2222
config: smoke.toml
2323
count: 1
2424
timeout: 10m
25+
- name: TestAptosSmoke
26+
config: smoke_aptos.toml
27+
count: 1
28+
timeout: 10m
2529
- name: TestSolanaSmoke
2630
config: smoke_solana.toml
2731
count: 1

book/src/SUMMARY.md

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,51 @@
22

33
- [Overview](./overview.md)
44
- [Framework](./framework/overview.md)
5-
- [Getting Started](./framework/getting_started.md)
6-
- [Your First Test](./framework/first_test.md)
7-
- [Connecting Chainlink Node](./framework/connecting_chainlink_node.md)
8-
- [Connecting Chainlink Node (Multiple networks)]()
9-
- [NodeSet Environment](./framework/nodeset_environment.md)
10-
- [NodeSet with Capabilities](./framework/nodeset_capabilities.md)
11-
- [NodeSet (Local Docker builds)](./framework/nodeset_docker_rebuild.md)
12-
- [NodeSet Compat Environment](./framework/nodeset_compatibility.md)
13-
- [Creating your own components](./developing/developing_components.md)
14-
- [Fork Testing](./framework/fork.md)
15-
- [Quick Contracts Deployment](./framework/quick_deployment.md)
16-
- [Verifying Contracts](./framework/verify.md)
17-
- [NodeSet with External Blockchain]()
18-
- [CLI](./framework/cli.md)
19-
- [Configuration](./framework/configuration.md)
20-
- [Test Configuration](./framework/test_configuration_overrides.md)
21-
- [Exposing Components](framework/components/state.md)
22-
- [Debugging Tests](framework/components/debug.md)
23-
- [Components Cleanup](framework/components/cleanup.md)
24-
- [Components Caching](framework/components/caching.md)
25-
- [Mocking Services](framework/components/mocking.md)
26-
- [Copying Files](framework/copying_files.md)
27-
- [External Environment](framework/components/external.md)
28-
- [Troubleshooting](framework/components/troubleshooting.md)
29-
- [Secrets]()
30-
- [Observability Stack](framework/observability/observability_stack.md)
31-
- [Metrics](framework/observability/metrics.md)
32-
- [Logs](framework/observability/logs.md)
33-
- [Profiling](framework/observability/profiling.md)
34-
- [Traces]()
35-
- [Blockscout](framework/observability/blockscout.md)
36-
- [Components](framework/components/overview.md)
37-
- [Blockchains](framework/components/blockchains/overview.md)
38-
- [EVM](framework/components/blockchains/evm.md)
39-
- [Solana](framework/components/blockchains/solana.md)
40-
- [Optimism Stack]()
41-
- [Arbitrum Stack]()
42-
- [Chainlink](framework/components/chainlink.md)
43-
- [Node](framework/components/chainlink/node.md)
44-
- [NodeSet](framework/components/chainlink/nodeset.md)
45-
- [Clients]()
46-
- [Chainlink]()
47-
- [RPC]()
48-
- [Loki]()
5+
- [Getting Started](./framework/getting_started.md)
6+
- [Your First Test](./framework/first_test.md)
7+
- [Connecting Chainlink Node](./framework/connecting_chainlink_node.md)
8+
- [Connecting Chainlink Node (Multiple networks)]()
9+
- [NodeSet Environment](./framework/nodeset_environment.md)
10+
- [NodeSet with Capabilities](./framework/nodeset_capabilities.md)
11+
- [NodeSet (Local Docker builds)](./framework/nodeset_docker_rebuild.md)
12+
- [NodeSet Compat Environment](./framework/nodeset_compatibility.md)
13+
- [Creating your own components](./developing/developing_components.md)
14+
- [Fork Testing](./framework/fork.md)
15+
- [Quick Contracts Deployment](./framework/quick_deployment.md)
16+
- [Verifying Contracts](./framework/verify.md)
17+
- [NodeSet with External Blockchain]()
18+
- [CLI](./framework/cli.md)
19+
- [Configuration](./framework/configuration.md)
20+
- [Test Configuration](./framework/test_configuration_overrides.md)
21+
- [Exposing Components](framework/components/state.md)
22+
- [Debugging Tests](framework/components/debug.md)
23+
- [Components Cleanup](framework/components/cleanup.md)
24+
- [Components Caching](framework/components/caching.md)
25+
- [Mocking Services](framework/components/mocking.md)
26+
- [Copying Files](framework/copying_files.md)
27+
- [External Environment](framework/components/external.md)
28+
- [Troubleshooting](framework/components/troubleshooting.md)
29+
- [Secrets]()
30+
- [Observability Stack](framework/observability/observability_stack.md)
31+
- [Metrics](framework/observability/metrics.md)
32+
- [Logs](framework/observability/logs.md)
33+
- [Profiling](framework/observability/profiling.md)
34+
- [Traces]()
35+
- [Blockscout](framework/observability/blockscout.md)
36+
- [Components](framework/components/overview.md)
37+
- [Blockchains](framework/components/blockchains/overview.md)
38+
- [EVM](framework/components/blockchains/evm.md)
39+
- [Solana](framework/components/blockchains/solana.md)
40+
- [Aptos](framework/components/blockchains/aptos.md)
41+
- [Optimism Stack]()
42+
- [Arbitrum Stack]()
43+
- [Chainlink](framework/components/chainlink.md)
44+
- [Node](framework/components/chainlink/node.md)
45+
- [NodeSet](framework/components/chainlink/nodeset.md)
46+
- [Clients]()
47+
- [Chainlink]()
48+
- [RPC]()
49+
- [Loki]()
4950
- [Testing Maturity Model](framework/testing.md)
5051
- [Smoke]()
5152
- [Performance]()
@@ -103,7 +104,9 @@
103104
- [Sentinel](./libs/sentinel.md)
104105

105106
---
107+
106108
- [Releasing modules](releasing_modules.md)
109+
107110
---
108111

109112
- [CTFv1 (Discouraged)](lib.md)
@@ -135,5 +138,7 @@
135138
- [Third party apps]()
136139
- [Test helpers](lib/docker/test_helpers.md)
137140
- [Logging](lib/logging.md)
141+
138142
---
143+
139144
- [Build info](build_info.md)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Aptos Blockchain Client
2+
3+
You need to turn `Rosetta` off to use this image! Image doesn't work with `OrbStack` currently.
4+
5+
Docker Desktop
6+
7+
![img.png](rosetta-settings.png)
8+
9+
Default image is `aptoslabs/tools:aptos-node-v1.18.0`
10+
11+
API is available on [localhost:8080](http://localhost:8080/v1)
12+
13+
## Configuration
14+
15+
```toml
16+
[blockchain_a]
17+
type = "aptos"
18+
image = "aptoslabs/tools:aptos-node-v1.18.0" # or aptoslabs/tools:nightly
19+
contracts_dir = "$your_dir"
20+
```
21+
22+
## Usage
23+
24+
```golang
25+
package examples
26+
27+
import (
28+
"github.com/go-resty/resty/v2"
29+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
30+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
31+
"github.com/stretchr/testify/require"
32+
"testing"
33+
)
34+
35+
type CfgAptos struct {
36+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
37+
}
38+
39+
func TestAptosSmoke(t *testing.T) {
40+
in, err := framework.Load[CfgAptos](t)
41+
require.NoError(t, err)
42+
43+
bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
44+
require.NoError(t, err)
45+
46+
// execute any additional commands, to deploy contracts or set up
47+
// network is already funded, here are the keys
48+
_ = blockchain.DefaultAptosAccount
49+
_ = blockchain.DefaultAptosPrivateKey
50+
51+
_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
52+
require.NoError(t, err)
53+
54+
t.Run("test something", func(t *testing.T) {
55+
// use internal URL to connect Chainlink nodes
56+
_ = bc.Nodes[0].DockerInternalHTTPUrl
57+
// use host URL to interact
58+
_ = bc.Nodes[0].HostHTTPUrl
59+
r := resty.New().SetBaseURL(bc.Nodes[0].HostHTTPUrl).EnableTrace()
60+
_, err := r.R().Get("/v1/transactions")
61+
require.NoError(t, err)
62+
})
63+
}
64+
```
65+
66+
## Test Private Keys
67+
68+
Default account is already funded with `100000000 Octas`
69+
70+
```
71+
Account: 0xa337b42bd0eecf8fb59ee5929ea4541904b3c35a642040223f3d26ab57f59d6e
72+
PrivateKey: 0xd477c65f88ed9e6d4ec6e2014755c3cfa3e0c44e521d0111a02868c5f04c41d4
73+
```
69.6 KB
Loading

framework/.changeset/v0.4.3.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add Aptos network support
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package blockchain
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/docker/docker/api/types/container"
7+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
8+
"github.com/testcontainers/testcontainers-go"
9+
"github.com/testcontainers/testcontainers-go/wait"
10+
"path/filepath"
11+
)
12+
13+
var (
14+
DefaultAptosAccount = "0xa337b42bd0eecf8fb59ee5929ea4541904b3c35a642040223f3d26ab57f59d6e"
15+
DefaultAptosPrivateKey = "0xd477c65f88ed9e6d4ec6e2014755c3cfa3e0c44e521d0111a02868c5f04c41d4"
16+
)
17+
18+
func defaultAptos(in *Input) {
19+
if in.Image == "" {
20+
in.Image = "aptoslabs/tools:aptos-node-v1.18.0"
21+
}
22+
if in.Port != "" {
23+
framework.L.Warn().Msg("'port' field is set but only default port can be used: 8080")
24+
}
25+
in.Port = "8080"
26+
}
27+
28+
func newAptos(in *Input) (*Output, error) {
29+
defaultAptos(in)
30+
ctx := context.Background()
31+
containerName := framework.DefaultTCName("blockchain-node")
32+
33+
absPath, err := filepath.Abs(in.ContractsDir)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
bindPort := fmt.Sprintf("%s/tcp", in.Port)
39+
40+
req := testcontainers.ContainerRequest{
41+
Image: in.Image,
42+
ExposedPorts: []string{in.Port},
43+
WaitingFor: wait.ForLog("Faucet is ready"),
44+
Name: containerName,
45+
Labels: framework.DefaultTCLabels(),
46+
Networks: []string{framework.DefaultNetworkName},
47+
NetworkAliases: map[string][]string{
48+
framework.DefaultNetworkName: {containerName},
49+
},
50+
HostConfigModifier: func(h *container.HostConfig) {
51+
h.PortBindings = framework.MapTheSamePort(bindPort)
52+
},
53+
ImagePlatform: "linux/amd64",
54+
Cmd: []string{
55+
"aptos",
56+
"node",
57+
"run-local-testnet",
58+
"--with-faucet",
59+
"--force-restart",
60+
"--bind-to",
61+
"0.0.0.0",
62+
},
63+
Files: []testcontainers.ContainerFile{
64+
{
65+
HostFilePath: absPath,
66+
ContainerFilePath: "/",
67+
},
68+
},
69+
}
70+
71+
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
72+
ContainerRequest: req,
73+
Started: true,
74+
})
75+
if err != nil {
76+
return nil, err
77+
}
78+
host, err := c.Host(ctx)
79+
if err != nil {
80+
return nil, err
81+
}
82+
cmdStr := []string{"aptos", "init", "--network=local", "--assume-yes", fmt.Sprintf("--private-key=%s", DefaultAptosPrivateKey)}
83+
_, err = framework.ExecContainer(containerName, cmdStr)
84+
if err != nil {
85+
return nil, err
86+
}
87+
fundCmd := []string{"aptos", "account", "fund-with-faucet", "--account", DefaultAptosAccount}
88+
_, err = framework.ExecContainer(containerName, fundCmd)
89+
if err != nil {
90+
return nil, err
91+
}
92+
return &Output{
93+
UseCache: true,
94+
Family: "aptos",
95+
ContainerName: containerName,
96+
Nodes: []*Node{
97+
{
98+
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, in.Port),
99+
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
100+
},
101+
},
102+
}, nil
103+
}

framework/components/blockchain/blockchain.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
// Input is a blockchain network configuration params
99
type Input struct {
1010
// Common EVM fields
11-
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana" envconfig:"net_type"`
11+
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos" envconfig:"net_type"`
1212
Image string `toml:"image"`
1313
PullImage bool `toml:"pull_image"`
1414
Port string `toml:"port"`
@@ -47,8 +47,6 @@ type Node struct {
4747
}
4848

4949
// NewBlockchainNetwork this is an abstraction that can spin up various blockchain network simulators
50-
// - Anvil
51-
// - Geth
5250
func NewBlockchainNetwork(in *Input) (*Output, error) {
5351
if in.Out != nil && in.Out.UseCache {
5452
return in.Out, nil
@@ -64,6 +62,8 @@ func NewBlockchainNetwork(in *Input) (*Output, error) {
6462
out, err = newBesu(in)
6563
case "solana":
6664
out, err = newSolana(in)
65+
case "aptos":
66+
out, err = newAptos(in)
6767
default:
6868
return nil, fmt.Errorf("blockchain type is not supported or empty, must be 'anvil' or 'geth'")
6969
}

framework/docker.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,52 @@ func BuildImage(dctx, dfile, nameAndTag string) error {
271271
return runCommand("docker", "build", "-t", nameAndTag, "-f", dfilePath, dctx)
272272
}
273273
}
274+
275+
// ExecContainer executes a command inside a running container by name and returns the combined stdout/stderr.
276+
func ExecContainer(containerName string, command []string) (string, error) {
277+
L.Info().Strs("Command", command).Str("ContainerName", containerName).Msg("Executing command")
278+
p, err := tc.NewDockerProvider()
279+
if err != nil {
280+
return "", err
281+
}
282+
ctx := context.Background()
283+
containers, err := p.Client().ContainerList(ctx, container.ListOptions{
284+
All: true,
285+
})
286+
if err != nil {
287+
return "", fmt.Errorf("failed to list containers: %w", err)
288+
}
289+
var containerID string
290+
for _, cont := range containers {
291+
for _, name := range cont.Names {
292+
if name == "/"+containerName {
293+
containerID = cont.ID
294+
break
295+
}
296+
}
297+
}
298+
if containerID == "" {
299+
return "", fmt.Errorf("container with name '%s' not found", containerName)
300+
}
301+
302+
execConfig := container.ExecOptions{
303+
Cmd: command,
304+
AttachStdout: true,
305+
AttachStderr: true,
306+
}
307+
execID, err := p.Client().ContainerExecCreate(ctx, containerID, execConfig)
308+
if err != nil {
309+
return "", fmt.Errorf("failed to create exec instance: %w", err)
310+
}
311+
resp, err := p.Client().ContainerExecAttach(ctx, execID.ID, container.ExecStartOptions{})
312+
if err != nil {
313+
return "", fmt.Errorf("failed to attach to exec instance: %w", err)
314+
}
315+
defer resp.Close()
316+
output, err := io.ReadAll(resp.Reader)
317+
if err != nil {
318+
return "", fmt.Errorf("failed to read exec output: %w", err)
319+
}
320+
L.Info().Str("Output", string(output)).Msg("Command output")
321+
return string(output), nil
322+
}

0 commit comments

Comments
 (0)