Skip to content

Commit

Permalink
Aptos network support (#1562)
Browse files Browse the repository at this point in the history
Aptos network support
  • Loading branch information
skudasov authored Jan 14, 2025
1 parent 9658154 commit a4290be
Show file tree
Hide file tree
Showing 17 changed files with 356 additions and 77 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/framework-golden-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jobs:
config: smoke.toml
count: 1
timeout: 10m
- name: TestAptosSmoke
config: smoke_aptos.toml
count: 1
timeout: 10m
- name: TestSolanaSmoke
config: smoke_solana.toml
count: 1
Expand Down
93 changes: 49 additions & 44 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,51 @@

- [Overview](./overview.md)
- [Framework](./framework/overview.md)
- [Getting Started](./framework/getting_started.md)
- [Your First Test](./framework/first_test.md)
- [Connecting Chainlink Node](./framework/connecting_chainlink_node.md)
- [Connecting Chainlink Node (Multiple networks)]()
- [NodeSet Environment](./framework/nodeset_environment.md)
- [NodeSet with Capabilities](./framework/nodeset_capabilities.md)
- [NodeSet (Local Docker builds)](./framework/nodeset_docker_rebuild.md)
- [NodeSet Compat Environment](./framework/nodeset_compatibility.md)
- [Creating your own components](./developing/developing_components.md)
- [Fork Testing](./framework/fork.md)
- [Quick Contracts Deployment](./framework/quick_deployment.md)
- [Verifying Contracts](./framework/verify.md)
- [NodeSet with External Blockchain]()
- [CLI](./framework/cli.md)
- [Configuration](./framework/configuration.md)
- [Test Configuration](./framework/test_configuration_overrides.md)
- [Exposing Components](framework/components/state.md)
- [Debugging Tests](framework/components/debug.md)
- [Components Cleanup](framework/components/cleanup.md)
- [Components Caching](framework/components/caching.md)
- [Mocking Services](framework/components/mocking.md)
- [Copying Files](framework/copying_files.md)
- [External Environment](framework/components/external.md)
- [Troubleshooting](framework/components/troubleshooting.md)
- [Secrets]()
- [Observability Stack](framework/observability/observability_stack.md)
- [Metrics](framework/observability/metrics.md)
- [Logs](framework/observability/logs.md)
- [Profiling](framework/observability/profiling.md)
- [Traces]()
- [Blockscout](framework/observability/blockscout.md)
- [Components](framework/components/overview.md)
- [Blockchains](framework/components/blockchains/overview.md)
- [EVM](framework/components/blockchains/evm.md)
- [Solana](framework/components/blockchains/solana.md)
- [Optimism Stack]()
- [Arbitrum Stack]()
- [Chainlink](framework/components/chainlink.md)
- [Node](framework/components/chainlink/node.md)
- [NodeSet](framework/components/chainlink/nodeset.md)
- [Clients]()
- [Chainlink]()
- [RPC]()
- [Loki]()
- [Getting Started](./framework/getting_started.md)
- [Your First Test](./framework/first_test.md)
- [Connecting Chainlink Node](./framework/connecting_chainlink_node.md)
- [Connecting Chainlink Node (Multiple networks)]()
- [NodeSet Environment](./framework/nodeset_environment.md)
- [NodeSet with Capabilities](./framework/nodeset_capabilities.md)
- [NodeSet (Local Docker builds)](./framework/nodeset_docker_rebuild.md)
- [NodeSet Compat Environment](./framework/nodeset_compatibility.md)
- [Creating your own components](./developing/developing_components.md)
- [Fork Testing](./framework/fork.md)
- [Quick Contracts Deployment](./framework/quick_deployment.md)
- [Verifying Contracts](./framework/verify.md)
- [NodeSet with External Blockchain]()
- [CLI](./framework/cli.md)
- [Configuration](./framework/configuration.md)
- [Test Configuration](./framework/test_configuration_overrides.md)
- [Exposing Components](framework/components/state.md)
- [Debugging Tests](framework/components/debug.md)
- [Components Cleanup](framework/components/cleanup.md)
- [Components Caching](framework/components/caching.md)
- [Mocking Services](framework/components/mocking.md)
- [Copying Files](framework/copying_files.md)
- [External Environment](framework/components/external.md)
- [Troubleshooting](framework/components/troubleshooting.md)
- [Secrets]()
- [Observability Stack](framework/observability/observability_stack.md)
- [Metrics](framework/observability/metrics.md)
- [Logs](framework/observability/logs.md)
- [Profiling](framework/observability/profiling.md)
- [Traces]()
- [Blockscout](framework/observability/blockscout.md)
- [Components](framework/components/overview.md)
- [Blockchains](framework/components/blockchains/overview.md)
- [EVM](framework/components/blockchains/evm.md)
- [Solana](framework/components/blockchains/solana.md)
- [Aptos](framework/components/blockchains/aptos.md)
- [Optimism Stack]()
- [Arbitrum Stack]()
- [Chainlink](framework/components/chainlink.md)
- [Node](framework/components/chainlink/node.md)
- [NodeSet](framework/components/chainlink/nodeset.md)
- [Clients]()
- [Chainlink]()
- [RPC]()
- [Loki]()
- [Testing Maturity Model](framework/testing.md)
- [Smoke]()
- [Performance]()
Expand Down Expand Up @@ -103,7 +104,9 @@
- [Sentinel](./libs/sentinel.md)

---

- [Releasing modules](releasing_modules.md)

---

- [CTFv1 (Discouraged)](lib.md)
Expand Down Expand Up @@ -135,5 +138,7 @@
- [Third party apps]()
- [Test helpers](lib/docker/test_helpers.md)
- [Logging](lib/logging.md)

---

- [Build info](build_info.md)
73 changes: 73 additions & 0 deletions book/src/framework/components/blockchains/aptos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Aptos Blockchain Client

You need to turn `Rosetta` off to use this image! Image doesn't work with `OrbStack` currently.

Docker Desktop

![img.png](rosetta-settings.png)

Default image is `aptoslabs/tools:aptos-node-v1.18.0`

API is available on [localhost:8080](http://localhost:8080/v1)

## Configuration

```toml
[blockchain_a]
type = "aptos"
image = "aptoslabs/tools:aptos-node-v1.18.0" # or aptoslabs/tools:nightly
contracts_dir = "$your_dir"
```

## Usage

```golang
package examples

import (
"github.com/go-resty/resty/v2"
"github.com/smartcontractkit/chainlink-testing-framework/framework"
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
"github.com/stretchr/testify/require"
"testing"
)

type CfgAptos struct {
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
}

func TestAptosSmoke(t *testing.T) {
in, err := framework.Load[CfgAptos](t)
require.NoError(t, err)

bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
require.NoError(t, err)

// execute any additional commands, to deploy contracts or set up
// network is already funded, here are the keys
_ = blockchain.DefaultAptosAccount
_ = blockchain.DefaultAptosPrivateKey

_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
require.NoError(t, err)

t.Run("test something", func(t *testing.T) {
// use internal URL to connect Chainlink nodes
_ = bc.Nodes[0].DockerInternalHTTPUrl
// use host URL to interact
_ = bc.Nodes[0].HostHTTPUrl
r := resty.New().SetBaseURL(bc.Nodes[0].HostHTTPUrl).EnableTrace()
_, err := r.R().Get("/v1/transactions")
require.NoError(t, err)
})
}
```

## Test Private Keys

Default account is already funded with `100000000 Octas`

```
Account: 0xa337b42bd0eecf8fb59ee5929ea4541904b3c35a642040223f3d26ab57f59d6e
PrivateKey: 0xd477c65f88ed9e6d4ec6e2014755c3cfa3e0c44e521d0111a02868c5f04c41d4
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions framework/.changeset/v0.4.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add Aptos network support
103 changes: 103 additions & 0 deletions framework/components/blockchain/aptos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package blockchain

import (
"context"
"fmt"
"github.com/docker/docker/api/types/container"
"github.com/smartcontractkit/chainlink-testing-framework/framework"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"path/filepath"
)

var (
DefaultAptosAccount = "0xa337b42bd0eecf8fb59ee5929ea4541904b3c35a642040223f3d26ab57f59d6e"
DefaultAptosPrivateKey = "0xd477c65f88ed9e6d4ec6e2014755c3cfa3e0c44e521d0111a02868c5f04c41d4"
)

func defaultAptos(in *Input) {
if in.Image == "" {
in.Image = "aptoslabs/tools:aptos-node-v1.18.0"
}
if in.Port != "" {
framework.L.Warn().Msg("'port' field is set but only default port can be used: 8080")
}
in.Port = "8080"
}

func newAptos(in *Input) (*Output, error) {
defaultAptos(in)
ctx := context.Background()
containerName := framework.DefaultTCName("blockchain-node")

absPath, err := filepath.Abs(in.ContractsDir)
if err != nil {
return nil, err
}

bindPort := fmt.Sprintf("%s/tcp", in.Port)

req := testcontainers.ContainerRequest{
Image: in.Image,
ExposedPorts: []string{in.Port},
WaitingFor: wait.ForLog("Faucet is ready"),
Name: containerName,
Labels: framework.DefaultTCLabels(),
Networks: []string{framework.DefaultNetworkName},
NetworkAliases: map[string][]string{
framework.DefaultNetworkName: {containerName},
},
HostConfigModifier: func(h *container.HostConfig) {
h.PortBindings = framework.MapTheSamePort(bindPort)
},
ImagePlatform: "linux/amd64",
Cmd: []string{
"aptos",
"node",
"run-local-testnet",
"--with-faucet",
"--force-restart",
"--bind-to",
"0.0.0.0",
},
Files: []testcontainers.ContainerFile{
{
HostFilePath: absPath,
ContainerFilePath: "/",
},
},
}

c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
return nil, err
}
host, err := c.Host(ctx)
if err != nil {
return nil, err
}
cmdStr := []string{"aptos", "init", "--network=local", "--assume-yes", fmt.Sprintf("--private-key=%s", DefaultAptosPrivateKey)}
_, err = framework.ExecContainer(containerName, cmdStr)
if err != nil {
return nil, err
}
fundCmd := []string{"aptos", "account", "fund-with-faucet", "--account", DefaultAptosAccount}
_, err = framework.ExecContainer(containerName, fundCmd)
if err != nil {
return nil, err
}
return &Output{
UseCache: true,
Family: "aptos",
ContainerName: containerName,
Nodes: []*Node{
{
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, in.Port),
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
},
},
}, nil
}
6 changes: 3 additions & 3 deletions framework/components/blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
// Input is a blockchain network configuration params
type Input struct {
// Common EVM fields
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana" envconfig:"net_type"`
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos" envconfig:"net_type"`
Image string `toml:"image"`
PullImage bool `toml:"pull_image"`
Port string `toml:"port"`
Expand Down Expand Up @@ -47,8 +47,6 @@ type Node struct {
}

// NewBlockchainNetwork this is an abstraction that can spin up various blockchain network simulators
// - Anvil
// - Geth
func NewBlockchainNetwork(in *Input) (*Output, error) {
if in.Out != nil && in.Out.UseCache {
return in.Out, nil
Expand All @@ -64,6 +62,8 @@ func NewBlockchainNetwork(in *Input) (*Output, error) {
out, err = newBesu(in)
case "solana":
out, err = newSolana(in)
case "aptos":
out, err = newAptos(in)
default:
return nil, fmt.Errorf("blockchain type is not supported or empty, must be 'anvil' or 'geth'")
}
Expand Down
49 changes: 49 additions & 0 deletions framework/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,52 @@ func BuildImage(dctx, dfile, nameAndTag string) error {
return runCommand("docker", "build", "-t", nameAndTag, "-f", dfilePath, dctx)
}
}

// ExecContainer executes a command inside a running container by name and returns the combined stdout/stderr.
func ExecContainer(containerName string, command []string) (string, error) {
L.Info().Strs("Command", command).Str("ContainerName", containerName).Msg("Executing command")
p, err := tc.NewDockerProvider()
if err != nil {
return "", err
}
ctx := context.Background()
containers, err := p.Client().ContainerList(ctx, container.ListOptions{
All: true,
})
if err != nil {
return "", fmt.Errorf("failed to list containers: %w", err)
}
var containerID string
for _, cont := range containers {
for _, name := range cont.Names {
if name == "/"+containerName {
containerID = cont.ID
break
}
}
}
if containerID == "" {
return "", fmt.Errorf("container with name '%s' not found", containerName)
}

execConfig := container.ExecOptions{
Cmd: command,
AttachStdout: true,
AttachStderr: true,
}
execID, err := p.Client().ContainerExecCreate(ctx, containerID, execConfig)
if err != nil {
return "", fmt.Errorf("failed to create exec instance: %w", err)
}
resp, err := p.Client().ContainerExecAttach(ctx, execID.ID, container.ExecStartOptions{})
if err != nil {
return "", fmt.Errorf("failed to attach to exec instance: %w", err)
}
defer resp.Close()
output, err := io.ReadAll(resp.Reader)
if err != nil {
return "", fmt.Errorf("failed to read exec output: %w", err)
}
L.Info().Str("Output", string(output)).Msg("Command output")
return string(output), nil
}
Loading

0 comments on commit a4290be

Please sign in to comment.