Skip to content

Commit 042c74e

Browse files
authored
Add blockchain anvil zksync (#1651)
* Add anvil zksync with default parameters This reverts commit a5582e3. * Change ports * Move together with other Anvil usages * Move back * Fork from testnet * Remove fork url * Lint * Review changes * Add docs * Refactor Besu * Refactor geth * Use with/without ws flag
1 parent 63eefaa commit 042c74e

File tree

9 files changed

+350
-211
lines changed

9 files changed

+350
-211
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
- [Aptos](framework/components/blockchains/aptos.md)
4545
- [Sui](framework/components/blockchains/sui.md)
4646
- [TRON](framework/components/blockchains/tron.md)
47+
- [ZKSync](framework/components/blockchains/zksync.md)
4748
- [Optimism Stack]()
4849
- [Arbitrum Stack]()
4950
- [Chainlink](framework/components/chainlink.md)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# ZKSync clients
2+
3+
We support [Anvil ZKSync](https://foundry-book.zksync.io/anvil-zksync/) memory blockchain in a Docker image.
4+
It is a fork of [Anvil](https://book.getfoundry.sh/anvil/) with support for ZK Sync transactions and ZK VM.
5+
6+
Components are managed as [EVM](./evm) components.
7+
8+
> The component will create a temporary Dockerfile that pulls the Anvil ZKSync executables from https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/install-foundry-zksync
9+
> as per ZK Sync documentation.
10+
11+
## Configuration
12+
13+
Use `type: 'anvil-zksync'` to use Anvil ZKSync.
14+
15+
The configurable arguments are two:
16+
17+
- `chain_id`, defaults to `"260"`
18+
- `port`, defaults to `"8011"`
19+
20+
## Test Private Keys
21+
22+
Testing keys are exported in the `blockchain` go module under `AnvilZKSyncRichAccountPks`.
Lines changed: 8 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
package blockchain
22

33
import (
4-
"context"
5-
"fmt"
64
"strings"
7-
"time"
8-
9-
"github.com/docker/docker/api/types/container"
10-
"github.com/docker/go-connections/nat"
11-
"github.com/testcontainers/testcontainers-go"
12-
"github.com/testcontainers/testcontainers-go/wait"
135

146
"github.com/smartcontractkit/chainlink-testing-framework/framework"
157
)
@@ -33,60 +25,18 @@ func defaultAnvil(in *Input) {
3325
// newAnvil deploy foundry anvil node
3426
func newAnvil(in *Input) (*Output, error) {
3527
defaultAnvil(in)
36-
ctx := context.Background()
28+
req := baseRequest(in, WithoutWsEndpoint)
29+
30+
req.Image = in.Image
31+
req.AlwaysPullImage = in.PullImage
32+
3733
entryPoint := []string{"anvil"}
3834
defaultCmd := []string{"--host", "0.0.0.0", "--port", in.Port, "--chain-id", in.ChainID}
3935
entryPoint = append(entryPoint, defaultCmd...)
4036
entryPoint = append(entryPoint, in.DockerCmdParamsOverrides...)
37+
req.Entrypoint = entryPoint
38+
4139
framework.L.Info().Any("Cmd", strings.Join(entryPoint, " ")).Msg("Creating anvil with command")
42-
bindPort := fmt.Sprintf("%s/tcp", in.Port)
43-
containerName := framework.DefaultTCName("blockchain-node")
4440

45-
req := testcontainers.ContainerRequest{
46-
AlwaysPullImage: in.PullImage,
47-
Image: in.Image,
48-
Labels: framework.DefaultTCLabels(),
49-
Name: containerName,
50-
ExposedPorts: []string{bindPort},
51-
HostConfigModifier: func(h *container.HostConfig) {
52-
h.PortBindings = framework.MapTheSamePort(bindPort)
53-
framework.ResourceLimitsFunc(h, in.ContainerResources)
54-
},
55-
Networks: []string{framework.DefaultNetworkName},
56-
NetworkAliases: map[string][]string{
57-
framework.DefaultNetworkName: {containerName},
58-
},
59-
WaitingFor: wait.ForListeningPort(nat.Port(in.Port)).WithStartupTimeout(10 * time.Second).WithPollInterval(200 * time.Millisecond),
60-
Entrypoint: entryPoint,
61-
}
62-
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
63-
ContainerRequest: req,
64-
Started: true,
65-
})
66-
if err != nil {
67-
return nil, err
68-
}
69-
host, err := framework.GetHost(c)
70-
if err != nil {
71-
return nil, err
72-
}
73-
mp, err := c.MappedPort(ctx, nat.Port(bindPort))
74-
if err != nil {
75-
return nil, err
76-
}
77-
return &Output{
78-
UseCache: true,
79-
Family: "evm",
80-
ChainID: in.ChainID,
81-
ContainerName: containerName,
82-
Container: c,
83-
Nodes: []*Node{
84-
{
85-
HostWSUrl: fmt.Sprintf("ws://%s:%s", host, mp.Port()),
86-
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, mp.Port()),
87-
DockerInternalWSUrl: fmt.Sprintf("ws://%s:%s", containerName, in.Port),
88-
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
89-
},
90-
},
91-
}, nil
41+
return createGenericEvmContainer(in, req, false)
9242
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package blockchain
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
8+
"github.com/testcontainers/testcontainers-go"
9+
10+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
11+
)
12+
13+
var AnvilZKSyncRichAccountPks = []string{
14+
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
15+
"59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
16+
"5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
17+
"7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6",
18+
"47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
19+
"8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba",
20+
"92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e",
21+
"4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356",
22+
"dbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97",
23+
"2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6",
24+
}
25+
26+
func defaultAnvilZksync(in *Input) {
27+
if in.ChainID == "" {
28+
in.ChainID = "260"
29+
}
30+
if in.Port == "" {
31+
in.Port = "8011"
32+
}
33+
}
34+
35+
const dockerFile = `FROM ubuntu:latest
36+
RUN apt update
37+
RUN apt install -y curl git
38+
RUN curl -L https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/install-foundry-zksync | bash`
39+
40+
// anvil-zskync has no public image builds. this method will build the image from source
41+
// creating a dockerfile in a temporary directory with the necessary commands to install
42+
// foundry-zksync.
43+
// see: https://foundry-book.zksync.io/getting-started/installation#using-foundry-with-docker
44+
func newAnvilZksync(in *Input) (*Output, error) {
45+
defaultAnvilZksync(in)
46+
req := baseRequest(in, WithoutWsEndpoint)
47+
48+
tempDir, err := os.MkdirTemp(".", "anvil-zksync-dockercontext")
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
defer func() {
54+
// delete the folder wether it was successful or not
55+
_ = os.RemoveAll(tempDir)
56+
}()
57+
58+
dockerfilePath := filepath.Join(tempDir, "anvilZksync.Dockerfile")
59+
60+
if err := os.WriteFile(dockerfilePath, []byte(dockerFile), 0600); err != nil {
61+
return nil, err
62+
}
63+
64+
req.FromDockerfile = testcontainers.FromDockerfile{
65+
Context: tempDir,
66+
Dockerfile: "anvilZksync.Dockerfile",
67+
KeepImage: true,
68+
}
69+
70+
req.Entrypoint = []string{
71+
"/bin/sh",
72+
"-c",
73+
"/root/.foundry/bin/anvil-zksync" +
74+
" --chain-id " + in.ChainID +
75+
" --port " + in.Port}
76+
77+
framework.L.Info().Any("Cmd", strings.Join(req.Entrypoint, " ")).Msg("Creating anvil zkSync with command")
78+
79+
output, err := createGenericEvmContainer(in, req, false)
80+
if err != nil {
81+
return nil, err
82+
}
83+
84+
return output, nil
85+
}
Lines changed: 7 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
11
package blockchain
22

3-
import (
4-
"context"
5-
"fmt"
6-
"time"
7-
8-
"github.com/docker/docker/api/types/container"
9-
10-
"github.com/smartcontractkit/chainlink-testing-framework/framework"
11-
12-
"github.com/docker/go-connections/nat"
13-
"github.com/testcontainers/testcontainers-go"
14-
"github.com/testcontainers/testcontainers-go/wait"
15-
)
16-
173
const (
184
DefaultBesuPrivateKey1 = "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"
195
DefaultBesuPrivateKey2 = "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3"
@@ -37,7 +23,11 @@ func defaultBesu(in *Input) {
3723

3824
func newBesu(in *Input) (*Output, error) {
3925
defaultBesu(in)
40-
ctx := context.Background()
26+
req := baseRequest(in, WithWsEndpoint)
27+
28+
req.Image = in.Image
29+
req.AlwaysPullImage = in.PullImage
30+
4131
defaultCmd := []string{
4232
"--network=dev",
4333
"--miner-enabled",
@@ -53,64 +43,7 @@ func newBesu(in *Input) (*Output, error) {
5343
"--data-path=/tmp/tmpDatdir",
5444
}
5545
entryPoint := append(defaultCmd, in.DockerCmdParamsOverrides...)
46+
req.Cmd = entryPoint
5647

57-
containerName := framework.DefaultTCName("blockchain-node")
58-
bindPort := fmt.Sprintf("%s/tcp", in.Port)
59-
bindPortWs := fmt.Sprintf("%s/tcp", in.WSPort)
60-
61-
req := testcontainers.ContainerRequest{
62-
AlwaysPullImage: in.PullImage,
63-
Image: in.Image,
64-
Name: containerName,
65-
ExposedPorts: []string{bindPort, bindPortWs},
66-
Networks: []string{framework.DefaultNetworkName},
67-
NetworkAliases: map[string][]string{
68-
framework.DefaultNetworkName: {containerName},
69-
},
70-
Labels: framework.DefaultTCLabels(),
71-
HostConfigModifier: func(h *container.HostConfig) {
72-
h.PortBindings = framework.MapTheSamePort(bindPortWs, bindPort)
73-
framework.ResourceLimitsFunc(h, in.ContainerResources)
74-
},
75-
WaitingFor: wait.ForListeningPort(nat.Port(in.Port)).WithStartupTimeout(15 * time.Second).WithPollInterval(200 * time.Millisecond),
76-
Cmd: entryPoint,
77-
}
78-
79-
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
80-
ContainerRequest: req,
81-
Started: true,
82-
})
83-
if err != nil {
84-
return nil, err
85-
}
86-
87-
host, err := c.Host(ctx)
88-
if err != nil {
89-
return nil, err
90-
}
91-
92-
mp, err := c.MappedPort(ctx, nat.Port(bindPort))
93-
if err != nil {
94-
return nil, err
95-
}
96-
mpWs, err := c.MappedPort(ctx, nat.Port(bindPortWs))
97-
if err != nil {
98-
return nil, err
99-
}
100-
101-
return &Output{
102-
UseCache: true,
103-
ChainID: in.ChainID,
104-
Family: "evm",
105-
ContainerName: containerName,
106-
Container: c,
107-
Nodes: []*Node{
108-
{
109-
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, mp.Port()),
110-
HostWSUrl: fmt.Sprintf("ws://%s:%s", host, mpWs.Port()),
111-
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
112-
DockerInternalWSUrl: fmt.Sprintf("ws://%s:%s", containerName, in.WSPort),
113-
},
114-
},
115-
}, nil
48+
return createGenericEvmContainer(in, req, true)
11649
}

framework/components/blockchain/blockchain.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func NewBlockchainNetwork(in *Input) (*Output, error) {
7777
out, err = newSui(in)
7878
case "tron":
7979
out, err = newTron(in)
80+
case "anvil-zksync":
81+
out, err = newAnvilZksync(in)
8082
default:
8183
return nil, fmt.Errorf("blockchain type is not supported or empty, must be 'anvil' or 'geth'")
8284
}

0 commit comments

Comments
 (0)