Skip to content

Commit f38d4e4

Browse files
NONEVM-1954: support multiple ton networks (#1862)
* feat: multiple ton networks * feat: less custom input * chore: clean up and tests * chore: native parallel tests * Revert "chore: native parallel tests" This reverts commit 91bcf3e. * chore: just two tons * chore: clean up * fix: remove deprecated network * changeset --------- Co-authored-by: skudasov <[email protected]>
1 parent 068b83b commit f38d4e4

File tree

6 files changed

+314
-73
lines changed

6 files changed

+314
-73
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ jobs:
4747
config: smoke_ton.toml
4848
count: 1
4949
timeout: 10m
50+
- name: TestTonParallel
51+
config: parallel_ton.toml
52+
count: 1
53+
timeout: 10m
5054
- name: TestUpgrade
5155
config: upgrade.toml
5256
count: 1

framework/.changeset/v0.9.1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Support multiple TON instances

framework/components/blockchain/ton.go

Lines changed: 107 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,35 @@ import (
44
"context"
55
"fmt"
66
"os"
7-
"os/exec"
8-
"strings"
7+
"strconv"
98
"time"
109

1110
"github.com/testcontainers/testcontainers-go"
11+
"github.com/testcontainers/testcontainers-go/network"
1212
"github.com/testcontainers/testcontainers-go/wait"
1313

1414
"github.com/smartcontractkit/chainlink-testing-framework/framework"
1515
)
1616

1717
const (
18-
// default ports from mylocalton-docker
19-
DefaultTonHTTPAPIPort = "8081"
2018
DefaultTonSimpleServerPort = "8000"
21-
DefaultTonTONExplorerPort = "8080"
22-
DefaultTonLiteServerPort = "40004"
23-
2419
// NOTE: Prefunded high-load wallet from MyLocalTon pre-funded wallet, that can send up to 254 messages per 1 external message
2520
// https://docs.ton.org/v3/documentation/smart-contracts/contracts-specs/highload-wallet#highload-wallet-v2
2621
DefaultTonHlWalletAddress = "-1:5ee77ced0b7ae6ef88ab3f4350d8872c64667ffbe76073455215d3cdfab3294b"
2722
DefaultTonHlWalletMnemonic = "twenty unfair stay entry during please water april fabric morning length lumber style tomorrow melody similar forum width ride render void rather custom coin"
2823
)
2924

3025
var (
31-
CommonDBVars = map[string]string{
32-
"POSTGRES_DIALECT": "postgresql+asyncpg",
33-
"POSTGRES_HOST": "index-postgres",
34-
"POSTGRES_PORT": "5432",
35-
"POSTGRES_USER": "postgres",
36-
"POSTGRES_DB": "ton_index",
37-
"POSTGRES_PASSWORD": "PostgreSQL1234",
38-
"POSTGRES_DBNAME": "ton_index",
39-
"TON_INDEXER_TON_HTTP_API_ENDPOINT": "http://tonhttpapi:8080/",
40-
"TON_INDEXER_IS_TESTNET": "0",
41-
"TON_INDEXER_REDIS_DSN": "redis://redis:6379",
42-
26+
commonDBVars = map[string]string{
27+
"POSTGRES_DIALECT": "postgresql+asyncpg",
28+
"POSTGRES_HOST": "index-postgres",
29+
"POSTGRES_PORT": "5432",
30+
"POSTGRES_USER": "postgres",
31+
"POSTGRES_DB": "ton_index",
32+
"POSTGRES_PASSWORD": "PostgreSQL1234",
33+
"POSTGRES_DBNAME": "ton_index",
34+
"TON_INDEXER_IS_TESTNET": "0",
35+
"TON_INDEXER_REDIS_DSN": "redis://redis:6379",
4336
"TON_WORKER_FROM": "1",
4437
"TON_WORKER_DBROOT": "/tondb",
4538
"TON_WORKER_BINARY": "ton-index-postgres-v2",
@@ -59,6 +52,18 @@ type containerTemplate struct {
5952
Alias string
6053
}
6154

55+
type hostPortMapping struct {
56+
SimpleServer string
57+
LiteServer string
58+
DHTServer string
59+
Console string
60+
ValidatorUDP string
61+
HTTPAPIPort string
62+
ExplorerPort string
63+
FaucetPort string
64+
IndexAPIPort string
65+
}
66+
6267
func commonContainer(
6368
ctx context.Context,
6469
name string,
@@ -91,83 +96,114 @@ func commonContainer(
9196
})
9297
}
9398

99+
func generateUniquePortsFromBase(basePort string) (*hostPortMapping, error) {
100+
base, err := strconv.Atoi(basePort)
101+
if err != nil {
102+
return nil, fmt.Errorf("invalid base port %s: %w", basePort, err)
103+
}
104+
105+
mapping := &hostPortMapping{
106+
SimpleServer: basePort,
107+
HTTPAPIPort: strconv.Itoa(base + 10),
108+
ExplorerPort: strconv.Itoa(base + 20),
109+
IndexAPIPort: strconv.Itoa(base + 30),
110+
FaucetPort: strconv.Itoa(base + 40),
111+
LiteServer: strconv.Itoa(base + 50),
112+
DHTServer: strconv.Itoa(base + 60),
113+
Console: strconv.Itoa(base + 70),
114+
ValidatorUDP: strconv.Itoa(base + 80),
115+
}
116+
117+
return mapping, nil
118+
}
119+
94120
func defaultTon(in *Input) {
95121
if in.Image == "" {
96122
in.Image = "ghcr.io/neodix42/mylocalton-docker:latest"
97123
}
124+
if in.Port == "" {
125+
in.Port = DefaultTonSimpleServerPort
126+
}
98127
}
99128

100129
func newTon(in *Input) (*Output, error) {
101130
defaultTon(in)
102131

132+
hostPorts, err := generateUniquePortsFromBase(in.Port)
133+
if err != nil {
134+
return nil, fmt.Errorf("failed to generate unique ports: %w", err)
135+
}
136+
103137
ctx := context.Background()
104138

105-
networkName := "ton"
106-
lightCLientSubNet := "172.28.0.0/16"
107-
//nolint:gosec
108-
cmd := exec.Command("docker", "network", "create",
109-
"--driver=bridge",
110-
"--attachable",
111-
fmt.Sprintf("--subnet=%s", lightCLientSubNet),
112-
"--label=framework=ctf",
113-
networkName,
139+
network, err := network.New(ctx,
140+
network.WithAttachable(),
141+
network.WithLabels(framework.DefaultTCLabels()),
114142
)
115-
output, err := cmd.CombinedOutput()
116-
framework.L.Info().Str("output", string(output)).Msg("TON Docker network created")
117143
if err != nil {
118-
if !strings.Contains(string(output), "already exists") {
119-
return nil, fmt.Errorf("failed to create docker network: %v", err)
120-
}
121-
framework.L.Info().Msgf("Network %q already exists, ignoring", networkName)
144+
return nil, fmt.Errorf("failed to create network: %w", err)
122145
}
123146

147+
networkName := network.Name
148+
framework.L.Info().Str("output", string(networkName)).Msg("TON Docker network created")
149+
124150
tonServices := []containerTemplate{
125151
{
152+
Name: fmt.Sprintf("TON-genesis-%s", networkName),
126153
Image: "ghcr.io/neodix42/mylocalton-docker:latest",
127-
Ports: []string{"8000:8000/tcp", "40004:40004/tcp", "40003:40003/udp", "40002:40002/tcp", "40001:40001/udp"},
154+
Ports: []string{
155+
fmt.Sprintf("%s:%s/tcp", hostPorts.SimpleServer, DefaultTonSimpleServerPort),
156+
// Note: LITE_PORT port is used by the lite-client to connect to the genesis node in config
157+
fmt.Sprintf("%s:%s/tcp", hostPorts.LiteServer, hostPorts.LiteServer),
158+
fmt.Sprintf("%s:40003/udp", hostPorts.DHTServer),
159+
fmt.Sprintf("%s:40002/tcp", hostPorts.Console),
160+
fmt.Sprintf("%s:40001/udp", hostPorts.ValidatorUDP),
161+
},
128162
Env: map[string]string{
129-
"GENESIS": "true",
130-
"NAME": "genesis",
163+
"GENESIS": "true",
164+
"NAME": "genesis",
165+
// Note: LITE_PORT port is used by the lite-client to connect to the genesis node in config
166+
"LITE_PORT": hostPorts.LiteServer,
131167
"CUSTOM_PARAMETERS": "--state-ttl 315360000 --archive-ttl 315360000",
132168
},
133169
WaitFor: wait.ForExec([]string{
134-
"/usr/local/bin/lite-client", "-a", "127.0.0.1:40004", "-b",
170+
"/usr/local/bin/lite-client", "-a", fmt.Sprintf("127.0.0.1:%s", hostPorts.LiteServer), "-b",
135171
"E7XwFSQzNkcRepUC23J2nRpASXpnsEKmyyHYV4u/FZY=", "-t", "3", "-c", "last",
136172
}).WithStartupTimeout(2 * time.Minute),
137173
Network: networkName,
138174
Alias: "genesis",
139175
Mounts: testcontainers.ContainerMounts{
140176
{
141-
Source: testcontainers.GenericVolumeMountSource{Name: "shared-data"},
177+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("shared-data-%s", networkName)},
142178
Target: "/usr/share/data",
143179
},
144180
{
145-
Source: testcontainers.GenericVolumeMountSource{Name: "ton-db"},
181+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("ton-db-%s", networkName)},
146182
Target: "/var/ton-work/db",
147183
},
148184
},
149185
},
150186
{
151187
Image: "redis:latest",
152-
Name: "redis",
188+
Name: fmt.Sprintf("TON-redis-%s", networkName),
153189
Network: networkName,
154190
Alias: "redis",
155191
},
156192
{
157193
Image: "postgres:17",
158-
Name: "index-postgres",
194+
Name: fmt.Sprintf("TON-index-postgres-%s", networkName),
159195
Network: networkName,
160196
Alias: "index-postgres",
161-
Env: CommonDBVars,
197+
Env: commonDBVars,
162198
Mounts: testcontainers.ContainerMounts{
163199
{
164-
Source: testcontainers.GenericVolumeMountSource{Name: "pg"},
200+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("pg-%s", networkName)},
165201
Target: "/var/lib/postgresql/data",
166202
},
167203
},
168204
},
169205
{
170-
Name: "tonhttpapi",
206+
Name: fmt.Sprintf("TON-tonhttpapi-%s", networkName),
171207
Image: "ghcr.io/neodix42/ton-http-api:latest",
172208
Env: map[string]string{
173209
"TON_API_LOGS_JSONIFY": "0",
@@ -177,29 +213,29 @@ func newTon(in *Input) (*Output, error) {
177213
"TON_API_GET_METHODS_ENABLED": "1",
178214
"TON_API_JSON_RPC_ENABLED": "1",
179215

180-
"POSTGRES_DIALECT": "postgresql+asyncpg",
181-
"POSTGRES_HOST": "index-postgres",
182-
"POSTGRES_PORT": "5432",
183-
"POSTGRES_USER": "postgres",
184-
"POSTGRES_PASSWORD": "PostgreSQL1234",
185-
"POSTGRES_DBNAME": "ton_index",
186-
"TON_INDEXER_IS_TESTNET": "0",
187-
"TON_INDEXER_REDIS_DSN": "redis://redis:6379",
216+
"POSTGRES_DIALECT": commonDBVars["POSTGRES_DIALECT"],
217+
"POSTGRES_HOST": commonDBVars["POSTGRES_HOST"],
218+
"POSTGRES_PORT": commonDBVars["POSTGRES_PORT"],
219+
"POSTGRES_USER": commonDBVars["POSTGRES_USER"],
220+
"POSTGRES_PASSWORD": commonDBVars["POSTGRES_PASSWORD"],
221+
"POSTGRES_DBNAME": commonDBVars["POSTGRES_DBNAME"],
222+
"TON_INDEXER_IS_TESTNET": commonDBVars["TON_INDEXER_IS_TESTNET"],
223+
"TON_INDEXER_REDIS_DSN": commonDBVars["TON_INDEXER_REDIS_DSN"],
188224
},
189225
Mounts: testcontainers.ContainerMounts{
190226
{
191-
Source: testcontainers.GenericVolumeMountSource{Name: "shared-data"},
227+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("shared-data-%s", networkName)},
192228
Target: "/usr/share/data",
193229
},
194230
},
195-
Ports: []string{"8081/tcp"},
231+
Ports: []string{fmt.Sprintf("%s:8081/tcp", hostPorts.HTTPAPIPort)},
196232
WaitFor: wait.ForHTTP("/healthcheck").WithStartupTimeout(90 * time.Second),
197233
Command: []string{"-c", "gunicorn -k uvicorn.workers.UvicornWorker -w 1 --bind 0.0.0.0:8081 pyTON.main:app"},
198234
Network: networkName,
199235
Alias: "tonhttpapi",
200236
},
201237
{
202-
Name: "faucet",
238+
Name: fmt.Sprintf("TON-faucet-%s", networkName),
203239
Image: "ghcr.io/neodix42/mylocalton-docker-faucet:latest",
204240
Env: map[string]string{
205241
"FAUCET_USE_RECAPTCHA": "false",
@@ -209,11 +245,11 @@ func newTon(in *Input) (*Output, error) {
209245
},
210246
Mounts: testcontainers.ContainerMounts{
211247
{
212-
Source: testcontainers.GenericVolumeMountSource{Name: "shared-data"},
248+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("shared-data-%s", networkName)},
213249
Target: "/usr/share/data",
214250
},
215251
},
216-
Ports: []string{"88/tcp"},
252+
Ports: []string{fmt.Sprintf("%s:88/tcp", hostPorts.FaucetPort)},
217253
WaitFor: wait.ForHTTP("/").WithStartupTimeout(90 * time.Second),
218254
Network: networkName,
219255
Alias: "faucet",
@@ -222,32 +258,32 @@ func newTon(in *Input) (*Output, error) {
222258

223259
tonIndexingAndObservability := []containerTemplate{
224260
{
225-
Name: "explorer",
261+
Name: fmt.Sprintf("TON-explorer-%s", networkName),
226262
Image: "ghcr.io/neodix42/mylocalton-docker-explorer:latest",
227263
Env: map[string]string{
228264
"SERVER_PORT": "8080",
229265
},
230266
Mounts: testcontainers.ContainerMounts{
231267
{
232-
Source: testcontainers.GenericVolumeMountSource{Name: "shared-data"},
268+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("shared-data-%s", networkName)},
233269
Target: "/usr/share/data",
234270
},
235271
},
236-
Ports: []string{"8080:8080/tcp"},
272+
Ports: []string{fmt.Sprintf("%s:8080/tcp", hostPorts.ExplorerPort)},
237273
Network: networkName,
238274
Alias: "explorer",
239275
},
240276
{
241-
Name: "index-worker",
277+
Name: fmt.Sprintf("TON-index-worker-%s", networkName),
242278
Image: "toncenter/ton-indexer-worker:v1.2.0-test",
243-
Env: CommonDBVars,
279+
Env: commonDBVars,
244280
Mounts: testcontainers.ContainerMounts{
245281
{
246-
Source: testcontainers.GenericVolumeMountSource{Name: "ton-db"},
282+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("ton-db-%s", networkName)},
247283
Target: "/tondb",
248284
},
249285
{
250-
Source: testcontainers.GenericVolumeMountSource{Name: "index-workdir"},
286+
Source: testcontainers.GenericVolumeMountSource{Name: fmt.Sprintf("index-workdir-%s", networkName)},
251287
Target: "/workdir",
252288
},
253289
},
@@ -257,20 +293,20 @@ func newTon(in *Input) (*Output, error) {
257293
Alias: "index-worker",
258294
},
259295
{
260-
Name: "index-api",
296+
Name: fmt.Sprintf("TON-index-api-%s", networkName),
261297
Image: "toncenter/ton-indexer-api:v1.2.0-test",
262-
Env: CommonDBVars,
263-
Ports: []string{"8082/tcp"},
298+
Env: commonDBVars,
299+
Ports: []string{fmt.Sprintf("%s:8082/tcp", hostPorts.IndexAPIPort)},
264300
WaitFor: wait.ForHTTP("/").
265301
WithStartupTimeout(90 * time.Second),
266302
Command: []string{"-bind", ":8082", "-prefork", "-threads", "4", "-v2", "http://tonhttpapi:8081/"},
267303
Network: networkName,
268304
Alias: "index-api",
269305
},
270306
{
271-
Name: "event-classifier",
307+
Name: fmt.Sprintf("TON-event-classifier-%s", networkName),
272308
Image: "toncenter/ton-indexer-classifier:v1.2.0-test",
273-
Env: CommonDBVars,
309+
Env: commonDBVars,
274310
WaitFor: wait.ForLog("Reading finished tasks").
275311
WithStartupTimeout(90 * time.Second),
276312
Command: []string{"--pool-size", "4", "--prefetch-size", "1000", "--batch-size", "100"},
@@ -310,10 +346,9 @@ func newTon(in *Input) (*Output, error) {
310346
Type: in.Type,
311347
Family: FamilyTon,
312348
ContainerName: name,
313-
// Note: in case we need 1+ validators, we need to modify the compose file
314349
Nodes: []*Node{{
315350
// Note: define if we need more access other than the global config(tonutils-go only uses liteclients defined in the config)
316-
ExternalHTTPUrl: fmt.Sprintf("%s:%s", "localhost", DefaultTonSimpleServerPort),
351+
ExternalHTTPUrl: fmt.Sprintf("%s:%s", "localhost", hostPorts.SimpleServer),
317352
InternalHTTPUrl: fmt.Sprintf("%s:%s", name, DefaultTonSimpleServerPort),
318353
}},
319354
}, nil
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[blockchain_a]
2+
type = "ton"
3+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
4+
port = "8000"
5+
6+
[blockchain_b]
7+
type = "ton"
8+
image = "ghcr.io/neodix42/mylocalton-docker:latest"
9+
port = "8001"

0 commit comments

Comments
 (0)