@@ -3,10 +3,10 @@ package blockchain
33import (
44 "context"
55 "fmt"
6- "os"
76 "strconv"
87 "time"
98
9+ "github.com/docker/docker/api/types/container"
1010 "github.com/testcontainers/testcontainers-go"
1111 "github.com/testcontainers/testcontainers-go/network"
1212 "github.com/testcontainers/testcontainers-go/wait"
@@ -22,99 +22,26 @@ const (
2222 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"
2323)
2424
25- var (
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" ,
36- "TON_WORKER_FROM" : "1" ,
37- "TON_WORKER_DBROOT" : "/tondb" ,
38- "TON_WORKER_BINARY" : "ton-index-postgres-v2" ,
39- "TON_WORKER_ADDITIONAL_ARGS" : "" ,
40- }
41- )
42-
43- type containerTemplate struct {
44- Name string
45- Image string
46- Env map [string ]string
47- Mounts []testcontainers.ContainerMount
48- Ports []string
49- WaitFor wait.Strategy
50- Command []string
51- Network string
52- Alias string
53- }
54-
5525type hostPortMapping struct {
5626 SimpleServer string
5727 LiteServer string
5828 DHTServer string
5929 Console string
6030 ValidatorUDP string
61- HTTPAPIPort string
62- ExplorerPort string
63- FaucetPort string
64- IndexAPIPort string
65- }
66-
67- func commonContainer (
68- ctx context.Context ,
69- name string ,
70- image string ,
71- env map [string ]string ,
72- mounts []testcontainers.ContainerMount ,
73- exposedPorts []string ,
74- waitStrategy wait.Strategy ,
75- command []string ,
76- network string ,
77- alias string ,
78- ) (testcontainers.Container , error ) {
79- req := testcontainers.ContainerRequest {
80- Name : name ,
81- Labels : framework .DefaultTCLabels (),
82- Image : image ,
83- Env : env ,
84- Mounts : mounts ,
85- ExposedPorts : exposedPorts ,
86- Networks : []string {network },
87- NetworkAliases : map [string ][]string {
88- network : {alias },
89- },
90- WaitingFor : waitStrategy ,
91- Cmd : command ,
92- }
93- return testcontainers .GenericContainer (ctx , testcontainers.GenericContainerRequest {
94- ContainerRequest : req ,
95- Started : true ,
96- })
9731}
9832
9933func generateUniquePortsFromBase (basePort string ) (* hostPortMapping , error ) {
10034 base , err := strconv .Atoi (basePort )
10135 if err != nil {
10236 return nil , fmt .Errorf ("invalid base port %s: %w" , basePort , err )
10337 }
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
38+ return & hostPortMapping {
39+ SimpleServer : basePort , // external HTTP → internal 8000
40+ LiteServer : strconv .Itoa (base + 10 ),
41+ DHTServer : strconv .Itoa (base + 20 ),
42+ Console : strconv .Itoa (base + 30 ),
43+ ValidatorUDP : strconv .Itoa (base + 40 ),
44+ }, nil
11845}
11946
12047func defaultTon (in * Input ) {
@@ -126,220 +53,82 @@ func defaultTon(in *Input) {
12653 }
12754}
12855
56+ // newTon starts only the genesis node and nothing else.
12957func newTon (in * Input ) (* Output , error ) {
13058 defaultTon (in )
13159
13260 hostPorts , err := generateUniquePortsFromBase (in .Port )
13361 if err != nil {
134- return nil , fmt . Errorf ( "failed to generate unique ports: %w" , err )
62+ return nil , err
13563 }
13664
13765 ctx := context .Background ()
13866
139- network , err := network .New (ctx ,
67+ net , err := network .New (ctx ,
14068 network .WithAttachable (),
14169 network .WithLabels (framework .DefaultTCLabels ()),
14270 )
14371 if err != nil {
144- return nil , fmt . Errorf ( "failed to create network: %w" , err )
72+ return nil , err
14573 }
146-
147- networkName := network .Name
148- framework .L .Info ().Str ("output" , string (networkName )).Msg ("TON Docker network created" )
149-
150- tonServices := []containerTemplate {
151- {
152- Name : fmt .Sprintf ("TON-genesis-%s" , networkName ),
153- Image : "ghcr.io/neodix42/mylocalton-docker:latest" ,
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- },
162- Env : map [string ]string {
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 ,
167- "CUSTOM_PARAMETERS" : "--state-ttl 315360000 --archive-ttl 315360000" ,
168- },
169- WaitFor : wait .ForExec ([]string {
170- "/usr/local/bin/lite-client" , "-a" , fmt .Sprintf ("127.0.0.1:%s" , hostPorts .LiteServer ), "-b" ,
171- "E7XwFSQzNkcRepUC23J2nRpASXpnsEKmyyHYV4u/FZY=" , "-t" , "3" , "-c" , "last" ,
172- }).WithStartupTimeout (2 * time .Minute ),
173- Network : networkName ,
174- Alias : "genesis" ,
175- Mounts : testcontainers.ContainerMounts {
176- {
177- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
178- Target : "/usr/share/data" ,
179- },
180- {
181- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("ton-db-%s" , networkName )},
182- Target : "/var/ton-work/db" ,
183- },
184- },
185- },
186- {
187- Image : "redis:latest" ,
188- Name : fmt .Sprintf ("TON-redis-%s" , networkName ),
189- Network : networkName ,
190- Alias : "redis" ,
191- },
192- {
193- Image : "postgres:17" ,
194- Name : fmt .Sprintf ("TON-index-postgres-%s" , networkName ),
195- Network : networkName ,
196- Alias : "index-postgres" ,
197- Env : commonDBVars ,
198- Mounts : testcontainers.ContainerMounts {
199- {
200- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("pg-%s" , networkName )},
201- Target : "/var/lib/postgresql/data" ,
202- },
203- },
204- },
205- {
206- Name : fmt .Sprintf ("TON-tonhttpapi-%s" , networkName ),
207- Image : "ghcr.io/neodix42/ton-http-api:latest" ,
208- Env : map [string ]string {
209- "TON_API_LOGS_JSONIFY" : "0" ,
210- "TON_API_LOGS_LEVEL" : "ERROR" ,
211- "TON_API_TONLIB_LITESERVER_CONFIG" : "/usr/share/data/global.config.json" ,
212- "TON_API_TONLIB_CDLL_PATH" : "/usr/share/data/libtonlibjson.so" ,
213- "TON_API_GET_METHODS_ENABLED" : "1" ,
214- "TON_API_JSON_RPC_ENABLED" : "1" ,
215-
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" ],
224- },
225- Mounts : testcontainers.ContainerMounts {
226- {
227- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
228- Target : "/usr/share/data" ,
229- },
230- },
231- Ports : []string {fmt .Sprintf ("%s:8081/tcp" , hostPorts .HTTPAPIPort )},
232- WaitFor : wait .ForHTTP ("/healthcheck" ).WithStartupTimeout (90 * time .Second ),
233- Command : []string {"-c" , "gunicorn -k uvicorn.workers.UvicornWorker -w 1 --bind 0.0.0.0:8081 pyTON.main:app" },
234- Network : networkName ,
235- Alias : "tonhttpapi" ,
236- },
237- {
238- Name : fmt .Sprintf ("TON-faucet-%s" , networkName ),
239- Image : "ghcr.io/neodix42/mylocalton-docker-faucet:latest" ,
240- Env : map [string ]string {
241- "FAUCET_USE_RECAPTCHA" : "false" ,
242- "RECAPTCHA_SITE_KEY" : "" ,
243- "RECAPTCHA_SECRET" : "" ,
244- "SERVER_PORT" : "88" ,
245- },
246- Mounts : testcontainers.ContainerMounts {
247- {
248- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
249- Target : "/usr/share/data" ,
250- },
251- },
252- Ports : []string {fmt .Sprintf ("%s:88/tcp" , hostPorts .FaucetPort )},
253- WaitFor : wait .ForHTTP ("/" ).WithStartupTimeout (90 * time .Second ),
254- Network : networkName ,
255- Alias : "faucet" ,
256- },
74+ networkName := net .Name
75+
76+ bindPorts := []string {
77+ fmt .Sprintf ("%s:%s/tcp" , hostPorts .SimpleServer , DefaultTonSimpleServerPort ),
78+ fmt .Sprintf ("%s:%s/tcp" , hostPorts .LiteServer , hostPorts .LiteServer ),
79+ fmt .Sprintf ("%s:40003/udp" , hostPorts .DHTServer ),
80+ fmt .Sprintf ("%s:40002/tcp" , hostPorts .Console ),
81+ fmt .Sprintf ("%s:40001/udp" , hostPorts .ValidatorUDP ),
25782 }
25883
259- tonIndexingAndObservability := []containerTemplate {
260- {
261- Name : fmt .Sprintf ("TON-explorer-%s" , networkName ),
262- Image : "ghcr.io/neodix42/mylocalton-docker-explorer:latest" ,
263- Env : map [string ]string {
264- "SERVER_PORT" : "8080" ,
265- },
266- Mounts : testcontainers.ContainerMounts {
267- {
268- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
269- Target : "/usr/share/data" ,
270- },
271- },
272- Ports : []string {fmt .Sprintf ("%s:8080/tcp" , hostPorts .ExplorerPort )},
273- Network : networkName ,
274- Alias : "explorer" ,
84+ req := testcontainers.ContainerRequest {
85+ Image : in .Image ,
86+ AlwaysPullImage : in .PullImage ,
87+ Name : framework .DefaultTCName ("ton-genesis" ),
88+ ExposedPorts : bindPorts ,
89+ Networks : []string {networkName },
90+ NetworkAliases : map [string ][]string {networkName : {"genesis" }},
91+ Labels : framework .DefaultTCLabels (),
92+ Env : map [string ]string {
93+ "GENESIS" : "true" ,
94+ "NAME" : "genesis" ,
95+ "LITE_PORT" : hostPorts .LiteServer ,
96+ "CUSTOM_PARAMETERS" : "--state-ttl 315360000 --archive-ttl 315360000" ,
27597 },
276- {
277- Name : fmt .Sprintf ("TON-index-worker-%s" , networkName ),
278- Image : "toncenter/ton-indexer-worker:v1.2.0-test" ,
279- Env : commonDBVars ,
280- Mounts : testcontainers.ContainerMounts {
281- {
282- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("ton-db-%s" , networkName )},
283- Target : "/tondb" ,
284- },
285- {
286- Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("index-workdir-%s" , networkName )},
287- Target : "/workdir" ,
288- },
98+ WaitingFor : wait .ForExec ([]string {
99+ "/usr/local/bin/lite-client" ,
100+ "-a" , fmt .Sprintf ("127.0.0.1:%s" , hostPorts .LiteServer ),
101+ "-b" , "E7XwFSQzNkcRepUC23J2nRpASXpnsEKmyyHYV4u/FZY=" ,
102+ "-t" , "3" , "-c" , "last" ,
103+ }).WithStartupTimeout (2 * time .Minute ),
104+ Mounts : testcontainers.ContainerMounts {
105+ {
106+ Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
107+ Target : "/usr/share/data" ,
108+ },
109+ {
110+ Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("ton-db-%s" , networkName )},
111+ Target : "/var/ton-work/db" ,
289112 },
290- WaitFor : wait .ForLog ("Starting indexing from seqno" ).WithStartupTimeout (90 * time .Second ),
291- Command : []string {"--working-dir" , "/workdir" , "--from" , "1" , "--threads" , "8" },
292- Network : networkName ,
293- Alias : "index-worker" ,
294- },
295- {
296- Name : fmt .Sprintf ("TON-index-api-%s" , networkName ),
297- Image : "toncenter/ton-indexer-api:v1.2.0-test" ,
298- Env : commonDBVars ,
299- Ports : []string {fmt .Sprintf ("%s:8082/tcp" , hostPorts .IndexAPIPort )},
300- WaitFor : wait .ForHTTP ("/" ).
301- WithStartupTimeout (90 * time .Second ),
302- Command : []string {"-bind" , ":8082" , "-prefork" , "-threads" , "4" , "-v2" , "http://tonhttpapi:8081/" },
303- Network : networkName ,
304- Alias : "index-api" ,
305113 },
306- {
307- Name : fmt .Sprintf ("TON-event-classifier-%s" , networkName ),
308- Image : "toncenter/ton-indexer-classifier:v1.2.0-test" ,
309- Env : commonDBVars ,
310- WaitFor : wait .ForLog ("Reading finished tasks" ).
311- WithStartupTimeout (90 * time .Second ),
312- Command : []string {"--pool-size" , "4" , "--prefetch-size" , "1000" , "--batch-size" , "100" },
313- Network : networkName ,
314- Alias : "event-classifier" ,
114+ HostConfigModifier : func (h * container.HostConfig ) {
115+ framework .ResourceLimitsFunc (h , in .ContainerResources )
315116 },
316117 }
317118
318- containers := make ([]testcontainers.Container , 0 )
319- for _ , s := range tonServices {
320- c , err := commonContainer (ctx , s .Name , s .Image , s .Env , s .Mounts , s .Ports , s .WaitFor , s .Command , s .Network , s .Alias )
321- if err != nil {
322- return nil , fmt .Errorf ("failed to start %s: %v" , s .Name , err )
323- }
324- containers = append (containers , c )
325- }
326- // no need for indexers and block explorers in CI
327- if os .Getenv ("CI" ) != "true" {
328- for _ , s := range tonIndexingAndObservability {
329- c , err := commonContainer (ctx , s .Name , s .Image , s .Env , s .Mounts , s .Ports , s .WaitFor , s .Command , s .Network , s .Alias )
330- if err != nil {
331- return nil , fmt .Errorf ("failed to start %s: %v" , s .Name , err )
332- }
333- containers = append (containers , c )
334- }
119+ c , err := testcontainers .GenericContainer (ctx , testcontainers.GenericContainerRequest {
120+ ContainerRequest : req ,
121+ Started : true ,
122+ })
123+ if err != nil {
124+ return nil , err
335125 }
336126
337- genesisTonContainer := containers [0 ]
338-
339- name , err := genesisTonContainer .Name (ctx )
127+ name , err := c .Name (ctx )
340128 if err != nil {
341129 return nil , err
342130 }
131+
343132 return & Output {
344133 UseCache : true ,
345134 ChainID : in .ChainID ,
0 commit comments