@@ -11,8 +11,8 @@ import (
1111 "time"
1212
1313 "github.com/avast/retry-go/v4"
14- "github.com/stretchr/testify/require"
1514 "github.com/testcontainers/testcontainers-go"
15+
1616 "github.com/xssnick/tonutils-go/address"
1717 "github.com/xssnick/tonutils-go/tlb"
1818 "github.com/xssnick/tonutils-go/ton"
@@ -46,10 +46,6 @@ type CTFChainProviderConfig struct {
4646 // Note: Only images from supportedTONImageRepository are supported.
4747 Image string
4848
49- // Optional: Retry count for APIClient. Default is 0 (unlimited retries).
50- // Set to positive value for specific retry count.
51- RetryCount int
52-
5349 // Optional: Custom environment variables to pass to the TON container.
5450 // Example: map[string]string{"NEXT_BLOCK_GENERATION_DELAY": "0.5"}
5551 CustomEnv map [string ]string
@@ -61,7 +57,7 @@ func (c CTFChainProviderConfig) validate() error {
6157 return errors .New ("sync.Once instance is required" )
6258 }
6359
64- if c .Image != "" && ! strings .Contains (c .Image , supportedTONImageRepository ) {
60+ if c .Image != "" && ! strings .HasPrefix (c .Image , supportedTONImageRepository ) {
6561 return fmt .Errorf ("unsupported image %q: must be from %s" , c .Image , supportedTONImageRepository )
6662 }
6763
@@ -99,7 +95,7 @@ func NewCTFChainProvider(
9995
10096// Initialize sets up the Ton chain by validating the configuration, starting a CTF container,
10197// generating a deployer signer account, and constructing the chain instance.
102- func (p * CTFChainProvider ) Initialize (_ context.Context ) (chain.BlockChain , error ) {
98+ func (p * CTFChainProvider ) Initialize (ctx context.Context ) (chain.BlockChain , error ) {
10399 if p .chain != nil {
104100 return * p .chain , nil // Already initialized
105101 }
@@ -110,14 +106,28 @@ func (p *CTFChainProvider) Initialize(_ context.Context) (chain.BlockChain, erro
110106
111107 // Get the Chain ID
112108 chainID , err := chainsel .GetChainIDFromSelector (p .selector )
113- require .NoError (p .t , err , "failed to get chain ID from selector" )
109+ if err != nil {
110+ return nil , fmt .Errorf ("failed to get chain ID from selector: %w" , err )
111+ }
112+
113+ url , nodeClient , err := p .startContainer (ctx , chainID )
114+ if err != nil {
115+ return nil , fmt .Errorf ("failed to start container: %w" , err )
116+ }
114117
115- url , nodeClient := p .startContainer (chainID )
116118 // mylocalton uses a global_id of -217 by default
117119 // https://github.com/neodix42/mylocalton-docker/blob/8f9c6ea27cd608dc6370c4191554b42b5a797905/docker/scripts/start-genesis.sh#L62
118- tonWallet := createTonWallet (p .t , nodeClient , wallet.ConfigV5R1Final {NetworkGlobalID : - 217 }, wallet .WithWorkchain (0 ))
120+ tonWallet , err := createTonWallet (nodeClient , wallet.ConfigV5R1Final {NetworkGlobalID : - 217 }, wallet .WithWorkchain (0 ))
121+ if err != nil {
122+ return nil , fmt .Errorf ("failed to create wallet: %w" , err )
123+ }
124+
119125 // airdrop the deployer wallet
120- fundTonWallets (p .t , nodeClient , []* address.Address {tonWallet .Address ()}, []tlb.Coins {tlb .MustFromTON ("1000" )})
126+ ferr := fundTonWallets (ctx , nodeClient , []* address.Address {tonWallet .Address ()}, []tlb.Coins {tlb .MustFromTON ("1000" )})
127+ if ferr != nil {
128+ return nil , fmt .Errorf ("failed to fund wallet: %w" , ferr )
129+ }
130+
121131 p .chain = & cldf_ton.Chain {
122132 ChainMetadata : cldf_ton.ChainMetadata {Selector : p .selector },
123133 Client : nodeClient ,
@@ -129,15 +139,17 @@ func (p *CTFChainProvider) Initialize(_ context.Context) (chain.BlockChain, erro
129139 return * p .chain , nil
130140}
131141
132- func (p * CTFChainProvider ) startContainer (chainID string ) (string , ton.APIClientWrapped ) {
142+ func (p * CTFChainProvider ) startContainer (ctx context. Context , chainID string ) (string , * ton.APIClient , error ) {
133143 var (
134144 attempts = uint (10 )
135145 url string
136146 )
137147
138148 // initialize the docker network used by CTF
139149 err := framework .DefaultNetwork (p .config .Once )
140- require .NoError (p .t , err )
150+ if err != nil {
151+ return "" , nil , fmt .Errorf ("failed to initialize default network: %w" , err )
152+ }
141153
142154 url , err = retry .DoWithData (func () (string , error ) {
143155 // Initialize a port for the container
@@ -162,100 +174,128 @@ func (p *CTFChainProvider) startContainer(chainID string) (string, ton.APIClient
162174
163175 return output .Nodes [0 ].ExternalHTTPUrl , nil
164176 },
165- retry .Context (p . t . Context () ),
177+ retry .Context (ctx ),
166178 retry .Attempts (attempts ),
167179 retry .Delay (1 * time .Second ),
168180 retry .DelayType (retry .FixedDelay ),
169181 retry .OnRetry (func (attempt uint , err error ) {
170182 p .t .Logf ("Attempt %d/%d: Failed to start CTF Ton container: %v" , attempt + 1 , attempts , err )
171183 }),
172184 )
173- require .NoError (p .t , err , "Failed to start CTF Ton container after %d attempts" , attempts )
185+ if err != nil {
186+ return "" , nil , fmt .Errorf ("failed to start CTF Ton container after %d attempts: %w" , attempts , err )
187+ }
174188
175- connectionPool , err := createLiteclientConnectionPool (p .t .Context (), url )
176- require .NoError (p .t , err )
189+ connectionPool , err := createLiteclientConnectionPool (ctx , url )
190+ if err != nil {
191+ return "" , nil , fmt .Errorf ("failed to create liteclient connection pool: %w" , err )
192+ }
177193
178194 client := ton .NewAPIClient (connectionPool , ton .ProofCheckPolicyFast )
179195
180196 // check connection, CTFv2 handles the readiness
181- mb := getMasterchainBlockID (p .t , client )
197+ mb , err := getMasterchainBlockID (ctx , client )
198+ if err != nil {
199+ return "" , nil , fmt .Errorf ("failed to get masterchain block ID: %w" , err )
200+ }
201+
182202 // set starting point to verify master block proofs chain
183203 client .SetTrustedBlock (mb )
184204
185- retryCount := p .getRetryCount ()
186-
187- return url , client .WithRetry (retryCount )
205+ return url , client , nil
188206}
189207
190208// Note: this utility functions can be replaced once we have in the chainlink-ton utils package
191- func createTonWallet (t * testing.T , client ton.APIClientWrapped , versionConfig wallet.VersionConfig , option wallet.Option ) * wallet.Wallet {
192- t .Helper ()
193-
209+ func createTonWallet (client ton.APIClientWrapped , versionConfig wallet.VersionConfig , option wallet.Option ) (* wallet.Wallet , error ) {
194210 seed := wallet .NewSeed ()
195211 rw , err := wallet .FromSeed (client , seed , versionConfig )
196- require .NoError (t , err )
212+ if err != nil {
213+ return nil , fmt .Errorf ("failed to create wallet from seed: %w" , err )
214+ }
197215 pw , perr := wallet .FromPrivateKeyWithOptions (client , rw .PrivateKey (), versionConfig , option )
198- require .NoError (t , perr )
216+ if perr != nil {
217+ return nil , fmt .Errorf ("failed to create wallet from private key: %w" , perr )
218+ }
199219
200- return pw
220+ return pw , nil
201221}
202222
203- func fundTonWallets (t * testing.T , client ton.APIClientWrapped , recipients []* address.Address , amounts []tlb.Coins ) {
204- t .Helper ()
223+ func fundTonWallets (ctx context.Context , client ton.APIClientWrapped , recipients []* address.Address , amounts []tlb.Coins ) error {
224+ if len (amounts ) != len (recipients ) {
225+ return errors .New ("recipients and amounts must have the same length" )
226+ }
205227
206- require .Len (t , amounts , len (recipients ), "recipients and amounts must have the same length" )
207228 // initialize the prefunded wallet(Highload-V2), for other wallets, see https://github.com/neodix42/mylocalton-docker#pre-installed-wallets
208229 version := wallet .HighloadV2Verified //nolint:staticcheck // SA1019: only available option in mylocalton-docker
209230 rawHlWallet , err := wallet .FromSeed (client , strings .Fields (blockchain .DefaultTonHlWalletMnemonic ), version )
210- require .NoError (t , err )
231+ if err != nil {
232+ return fmt .Errorf ("failed to create wallet from seed: %w" , err )
233+ }
211234
212235 mcFunderWallet , err := wallet .FromPrivateKeyWithOptions (client , rawHlWallet .PrivateKey (), version , wallet .WithWorkchain (- 1 ))
213- require .NoError (t , err )
236+ if err != nil {
237+ return fmt .Errorf ("failed to create wallet from private key: %w" , err )
238+ }
214239
215240 funder , err := mcFunderWallet .GetSubwallet (uint32 (42 ))
216- require .NoError (t , err )
241+ if err != nil {
242+ return fmt .Errorf ("failed to get subwallet: %w" , err )
243+ }
244+
217245 // double check funder address
218- require .Equal (t , blockchain .DefaultTonHlWalletAddress , funder .Address ().StringRaw (), "funder address mismatch" )
246+ if funder .Address ().StringRaw () != blockchain .DefaultTonHlWalletAddress {
247+ return fmt .Errorf ("funder address mismatch: %s != %s" , funder .Address ().StringRaw (), blockchain .DefaultTonHlWalletAddress )
248+ }
249+
219250 // create transfer messages for each recipient
220251 messages := make ([]* wallet.Message , len (recipients ))
221252 for i , addr := range recipients {
222253 transfer , terr := funder .BuildTransfer (addr , amounts [i ], false , "" )
223- require .NoError (t , terr )
254+ if terr != nil {
255+ return fmt .Errorf ("failed to build transfer: %w" , terr )
256+ }
224257 messages [i ] = transfer
225258 }
226- _ , _ , txerr := funder .SendManyWaitTransaction (t .Context (), messages )
227- require .NoError (t , txerr , "airdrop transaction failed" )
259+
228260 // we don't wait for the transaction to be confirmed here, as it may take some time
229- }
261+ // the name SendManyWaitTransaction is misleading, it doesn't wait for the transaction to be confirmed,
262+ // it just sends the transactions(TON has asynchronous transactions)
263+ _ , _ , txerr := funder .SendManyWaitTransaction (ctx , messages )
264+ if txerr != nil {
265+ return fmt .Errorf ("failed to send many wait transaction: %w" , txerr )
266+ }
230267
231- func getMasterchainBlockID ( t * testing. T , client * ton. APIClient ) * ton. BlockIDExt {
232- t . Helper ()
268+ return nil
269+ }
233270
271+ func getMasterchainBlockID (ctx context.Context , client ton.APIClientWrapped ) (* ton.BlockIDExt , error ) {
234272 var masterchainBlockID * ton.BlockIDExt
235273 // check connection, CTFv2 handles the readiness
236274 err := retry .Do (func () error {
237275 var err error
238- masterchainBlockID , err = client .GetMasterchainInfo (t . Context () )
276+ masterchainBlockID , err = client .GetMasterchainInfo (ctx )
239277
240278 return err
241279 },
242- retry .Context (t . Context () ),
280+ retry .Context (ctx ),
243281 retry .Attempts (30 ),
244282 retry .Delay (1 * time .Second ),
245283 retry .DelayType (retry .FixedDelay ),
246284 )
247- require .NoError (t , err , "TON network not ready" )
285+ if err != nil {
286+ return nil , fmt .Errorf ("failed to get masterchain info: %w" , err )
287+ }
248288
249289 // return masterchain block for setting trusted block
250- return masterchainBlockID
290+ return masterchainBlockID , nil
251291}
252292
253293// Name returns the name of the CTFChainProvider.
254294func (* CTFChainProvider ) Name () string {
255- return "Ton CTF Chain Provider"
295+ return "TON CTF Chain Provider"
256296}
257297
258- // ChainSelector returns the chain selector of the Aptos chain managed by this provider.
298+ // ChainSelector returns the chain selector of the TON chain managed by this provider.
259299func (p * CTFChainProvider ) ChainSelector () uint64 {
260300 return p .selector
261301}
@@ -266,10 +306,6 @@ func (p *CTFChainProvider) BlockChain() chain.BlockChain {
266306 return * p .chain
267307}
268308
269- func (p * CTFChainProvider ) getRetryCount () int {
270- return p .config .RetryCount
271- }
272-
273309// getImage returns the configured Docker image, or the default if not specified.
274310func (p * CTFChainProvider ) getImage () string {
275311 if p .config .Image != "" {
0 commit comments