@@ -2,15 +2,17 @@ package blockchain
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
7+ "net/http"
8+ "net/url"
69 "strconv"
710 "time"
811
912 "github.com/docker/docker/api/types/container"
1013 "github.com/testcontainers/testcontainers-go"
1114 "github.com/testcontainers/testcontainers-go/network"
1215 "github.com/testcontainers/testcontainers-go/wait"
13- "github.com/xssnick/tonutils-go/liteclient"
1416
1517 "github.com/smartcontractkit/chainlink-testing-framework/framework"
1618)
@@ -25,13 +27,60 @@ const (
2527 liteServerPortOffset = 100 // internal, arbitrary offset for lite server port
2628)
2729
28- // intToIP4 converts int64 IP to string format (matches tonutils-go implementation)
29- func intToIP4 (ipInt int64 ) string {
30- b0 := strconv .FormatInt ((ipInt >> 24 )& 0xff , 10 )
31- b1 := strconv .FormatInt ((ipInt >> 16 )& 0xff , 10 )
32- b2 := strconv .FormatInt ((ipInt >> 8 )& 0xff , 10 )
33- b3 := strconv .FormatInt ((ipInt & 0xff ), 10 )
34- return b0 + "." + b1 + "." + b2 + "." + b3
30+ // TON config structures (e.g.: ton-blockchain.github.io/testnet-global.config.json)
31+ type tonLiteServer struct {
32+ IP int64 `json:"ip"`
33+ Port int `json:"port"`
34+ ID struct {
35+ Key string `json:"key"`
36+ Type string `json:"@type"`
37+ } `json:"id"`
38+ }
39+
40+ type tonConfig struct {
41+ LiteServers []tonLiteServer `json:"liteservers"`
42+ }
43+
44+ // convert int64 IP to string format (matches https://github.com/xssnick/tonutils-go/liteclient/connection.go/intToIP4)
45+ func intToIP4 (ip int64 ) string {
46+ uip := uint32 (ip ) //nolint:gosec // IP conversion is safe for TON format
47+ return fmt .Sprintf ("%d.%d.%d.%d" ,
48+ (uip >> 24 )& 0xFF ,
49+ (uip >> 16 )& 0xFF ,
50+ (uip >> 8 )& 0xFF ,
51+ uip & 0xFF )
52+ }
53+
54+ // fetch and parse TON config to generate liteserver URLs
55+ func fetchTonConfig (configURL string ) ([]string , error ) {
56+ parsedURL , err := url .Parse (configURL )
57+ if err != nil {
58+ return nil , fmt .Errorf ("invalid config URL: %w" , err )
59+ }
60+ if parsedURL .Scheme != "http" && parsedURL .Scheme != "https" {
61+ return nil , fmt .Errorf ("invalid URL scheme: %s" , parsedURL .Scheme )
62+ }
63+
64+ resp , err := http .Get (configURL ) //nolint:gosec // URL is validated above
65+ if err != nil {
66+ return nil , fmt .Errorf ("failed to fetch config: %w" , err )
67+ }
68+ defer resp .Body .Close ()
69+ defer resp .Body .Close ()
70+
71+ var config tonConfig
72+ if err := json .NewDecoder (resp .Body ).Decode (& config ); err != nil {
73+ return nil , fmt .Errorf ("failed to decode config: %w" , err )
74+ }
75+
76+ var liteServerURLs []string
77+ for _ , ls := range config .LiteServers {
78+ ipStr := intToIP4 (ls .IP )
79+ url := fmt .Sprintf ("liteserver://%s@%s:%d" , ls .ID .Key , ipStr , ls .Port )
80+ liteServerURLs = append (liteServerURLs , url )
81+ }
82+
83+ return liteServerURLs , nil
3584}
3685
3786type portMapping struct {
@@ -114,11 +163,11 @@ func newTon(in *Input) (*Output, error) {
114163 "-t" , "3" , "-c" , "last" ,
115164 }).WithStartupTimeout (2 * time .Minute ),
116165 Mounts : testcontainers.ContainerMounts {
117- testcontainers. ContainerMount {
166+ {
118167 Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("shared-data-%s" , networkName )},
119168 Target : "/usr/share/data" ,
120169 },
121- testcontainers. ContainerMount {
170+ {
122171 Source : testcontainers.GenericVolumeMountSource {Name : fmt .Sprintf ("ton-db-%s" , networkName )},
123172 Target : "/var/ton-work/db" ,
124173 },
@@ -141,28 +190,18 @@ func newTon(in *Input) (*Output, error) {
141190 return nil , err
142191 }
143192
144- // Fetch config using tonutils-go
193+ // fetch config and generate liteserver URLs from actual config
145194 configURL := fmt .Sprintf ("http://localhost:%s/localhost.global.config.json" , ports .SimpleServer )
146195
147- config , err := liteclient . GetConfigFromUrl ( ctx , configURL )
196+ liteServerURLs , err := fetchTonConfig ( configURL )
148197 if err != nil {
149- return nil , fmt . Errorf ( "failed to fetch TON config: %w" , err )
198+ return nil , err
150199 }
151200
152- if len (config . Liteservers ) == 0 {
201+ if len (liteServerURLs ) == 0 {
153202 return nil , fmt .Errorf ("no liteservers found in config" )
154203 }
155204
156- // Use the first liteserver to create URLs
157- ls := config .Liteservers [0 ]
158- ipStr := intToIP4 (ls .IP )
159- publicKey := ls .ID .Key
160- port := ls .Port
161-
162- // Create external and internal URLs
163- externalURL := fmt .Sprintf ("liteserver://%s@%s:%d" , publicKey , ipStr , port )
164- internalURL := fmt .Sprintf ("liteserver://%s@%s:%d" , publicKey , name , port )
165-
166205 return & Output {
167206 UseCache : true ,
168207 ChainID : in .ChainID ,
@@ -171,9 +210,9 @@ func newTon(in *Input) (*Output, error) {
171210 ContainerName : name ,
172211 Container : c ,
173212 Nodes : []* Node {{
174- // URLs now contain liteserver://publickey@host:port connections
175- ExternalHTTPUrl : externalURL ,
176- InternalHTTPUrl : internalURL ,
213+ // URLs now contain liteserver://publickey@host:port
214+ ExternalHTTPUrl : liteServerURLs [ 0 ] ,
215+ InternalHTTPUrl : liteServerURLs [ 0 ] ,
177216 }},
178217 }, nil
179218}
0 commit comments