-
Notifications
You must be signed in to change notification settings - Fork 44
Ton Network Support #1823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Ton Network Support #1823
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
53de762
feat: ton support
jadepark-dev 7ddfc8d
Merge branch 'main' into jade/ton-support
jadepark-dev f40ade4
feat: pack default faucet account with blockchain
jadepark-dev 59137cc
chore: remove temp file
jadepark-dev 5b2c9eb
chore: clean up
jadepark-dev 3d7d3d2
chore: clean up
jadepark-dev ee4bb23
chore: clean up
jadepark-dev c6a826f
chore: doc
jadepark-dev 9508064
chore: add doc details
jadepark-dev 0b46172
chore: clean up
jadepark-dev 448c9fa
Merge branch 'main' into jade/ton-support
jadepark-dev a661100
chore: gomods tidy
jadepark-dev a68baf0
chore: rename
jadepark-dev 63882c2
chore: goimports
jadepark-dev 440cb30
Merge branch 'main' into jade/ton-support
jadepark-dev c354cef
fix: only expose necessary ap
jadepark-dev e3439f4
chore: update readme
jadepark-dev 5d2dd53
Merge branch 'main' into jade/ton-support
jadepark-dev 48fb64b
Merge branch 'main' into jade/ton-support
jadepark-dev 6727c8d
feat: add input.DockerComposeFileURL
jadepark-dev 4578737
Merge branch 'jade/ton-support' of github.com:smartcontractkit/chainl…
jadepark-dev a121ef7
fat: add genesis node to the network
jadepark-dev f24b834
chore: testing nodeset
jadepark-dev e21c95e
chore: clean up
jadepark-dev 270a5a3
add changeset
skudasov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| # TON Blockchain Client | ||
|
|
||
| TON (The Open Network) support in the framework utilizes MyLocalTon Docker Compose environment to provide a local TON blockchain for testing purposes. | ||
|
|
||
| ## Configuration | ||
|
|
||
| ```toml | ||
| [blockchain_a] | ||
| type = "ton" | ||
| # By default uses MyLocalTon Docker Compose file | ||
| image = "https://raw.githubusercontent.com/neodix42/mylocalton-docker/main/docker-compose.yaml" | ||
| # Optional: Specify only core services needed for testing (useful in CI environments) | ||
| ton_core_services = [ | ||
| "genesis", | ||
| "tonhttpapi", | ||
| "event-cache", | ||
| "index-postgres", | ||
| "index-worker", | ||
| "index-api" | ||
| ] | ||
| ``` | ||
|
|
||
| ## Default Ports | ||
|
|
||
| The TON implementation exposes several services: | ||
|
|
||
| - TON Lite Server: Port 40004 | ||
| - TON HTTP API: Port 8081 | ||
| - TON Simple HTTP Server: Port 8000 | ||
| - TON Explorer: Port 8080 | ||
|
|
||
| > **Note**: By default, only the lite client service is exposed externally. Other services may need additional configuration to be accessible outside the Docker network. | ||
|
|
||
| ## Validator Configuration | ||
|
|
||
| By default, the MyLocalTon environment starts with only one validator enabled. If multiple validators are needed (up to 6 are supported), the Docker Compose file must be provided with modified version with corresponding service definition in toml file before starting the environment. | ||
|
|
||
| ## Usage | ||
|
|
||
| ```go | ||
| package examples | ||
|
|
||
| import ( | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| "github.com/xssnick/tonutils-go/liteclient" | ||
| "github.com/xssnick/tonutils-go/ton" | ||
| "github.com/xssnick/tonutils-go/ton/wallet" | ||
|
|
||
| "github.com/smartcontractkit/chainlink-testing-framework/framework" | ||
| "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" | ||
| ) | ||
|
|
||
| type CfgTon struct { | ||
| BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` | ||
| } | ||
|
|
||
| func TestTonSmoke(t *testing.T) { | ||
| in, err := framework.Load[CfgTon](t) | ||
| require.NoError(t, err) | ||
|
|
||
| bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) | ||
| require.NoError(t, err) | ||
|
|
||
| var client ton.APIClientWrapped | ||
|
|
||
| t.Run("setup:connect", func(t *testing.T) { | ||
| // Create a connection pool | ||
| connectionPool := liteclient.NewConnectionPool() | ||
|
|
||
| // Get the network configuration from the global config URL | ||
| cfg, cferr := liteclient.GetConfigFromUrl(t.Context(), fmt.Sprintf("http://%s/localhost.global.config.json", bc.Nodes[0].ExternalHTTPUrl)) | ||
| require.NoError(t, cferr, "Failed to get config from URL") | ||
|
|
||
| // Add connections from the config | ||
| caerr := connectionPool.AddConnectionsFromConfig(t.Context(), cfg) | ||
| require.NoError(t, caerr, "Failed to add connections from config") | ||
|
|
||
| // Create an API client with retry functionality | ||
| client = ton.NewAPIClient(connectionPool).WithRetry() | ||
|
|
||
| t.Run("setup:faucet", func(t *testing.T) { | ||
| // Create a wallet from the pre-funded high-load wallet seed | ||
| rawHlWallet, err := wallet.FromSeed(client, strings.Fields(blockchain.DefaultTonHlWalletMnemonic), wallet.HighloadV2Verified) | ||
| require.NoError(t, err, "failed to create highload wallet") | ||
|
|
||
| // Create a workchain -1 (masterchain) wallet | ||
| mcFunderWallet, err := wallet.FromPrivateKeyWithOptions(client, rawHlWallet.PrivateKey(), wallet.HighloadV2Verified, wallet.WithWorkchain(-1)) | ||
| require.NoError(t, err, "failed to create highload wallet") | ||
|
|
||
| // Get subwallet with ID 42 | ||
| funder, err := mcFunderWallet.GetSubwallet(uint32(42)) | ||
| require.NoError(t, err, "failed to get highload subwallet") | ||
|
|
||
| // Verify the funder address matches the expected default | ||
| require.Equal(t, funder.Address().StringRaw(), blockchain.DefaultTonHlWalletAddress, "funder address mismatch") | ||
|
|
||
| // Check the funder balance | ||
| master, err := client.GetMasterchainInfo(t.Context()) | ||
| require.NoError(t, err, "failed to get masterchain info for funder balance check") | ||
| funderBalance, err := funder.GetBalance(t.Context(), master) | ||
| require.NoError(t, err, "failed to get funder balance") | ||
| require.Equal(t, funderBalance.Nano().String(), "1000000000000000", "funder balance mismatch") | ||
| }) | ||
| }) | ||
| } | ||
| ``` | ||
|
|
||
| ## Test Private Keys | ||
|
|
||
| The framework includes a pre-funded high-load wallet for testing purposes. This wallet type can send up to 254 messages per 1 external message, making it efficient for test scenarios. | ||
|
|
||
| Default High-Load Wallet: | ||
| ``` | ||
| Address: -1:5ee77ced0b7ae6ef88ab3f4350d8872c64667ffbe76073455215d3cdfab3294b | ||
| Mnemonic: twenty unfair stay entry during please water april fabric morning length lumber style tomorrow melody similar forum width ride render void rather custom coin | ||
| ``` | ||
|
|
||
| ## Available Pre-funded Wallets | ||
|
|
||
| MyLocalTon Docker environment comes with several pre-funded wallets that can be used for testing: | ||
|
|
||
| 1. Genesis Wallet (V3R2, WalletId: 42) | ||
| 2. Validator Wallets (1-5) (V3R2, WalletId: 42) | ||
| 3. Faucet Wallet (V3R2, WalletId: 42, Balance: 1 million TON) | ||
| 4. Faucet Highload Wallet (Highload V2, QueryId: 0, Balance: 1 million TON) | ||
| 5. Basechain Faucet Wallet (V3R2, WalletId: 42, Balance: 1 million TON) | ||
| 6. Basechain Faucet Highload Wallet (Highload V2, QueryId: 0, Balance: 1 million TON) | ||
|
|
||
| For the complete list of addresses and mnemonics, refer to the [MyLocalTon Docker documentation](https://github.com/neodix42/mylocalton-docker). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| package blockchain | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| "os" | ||
| "path/filepath" | ||
| "time" | ||
|
|
||
| "github.com/docker/go-connections/nat" | ||
| "github.com/testcontainers/testcontainers-go/modules/compose" | ||
| "github.com/testcontainers/testcontainers-go/wait" | ||
|
|
||
| "github.com/smartcontractkit/chainlink-testing-framework/framework" | ||
| ) | ||
|
|
||
| const ( | ||
| // default ports from mylocalton-docker | ||
| DefaultTonHTTPAPIPort = "8081" | ||
| DefaultTonSimpleServerPort = "8000" | ||
| DefaultTonTONExplorerPort = "8080" | ||
| DefaultTonLiteServerPort = "40004" | ||
|
|
||
| // NOTE: Prefunded high-load wallet from MyLocalTon pre-funded wallet, that can send up to 254 messages per 1 external message | ||
| // https://docs.ton.org/v3/documentation/smart-contracts/contracts-specs/highload-wallet#highload-wallet-v2 | ||
| DefaultTonHlWalletAddress = "-1:5ee77ced0b7ae6ef88ab3f4350d8872c64667ffbe76073455215d3cdfab3294b" | ||
| 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" | ||
| ) | ||
|
|
||
| func defaultTon(in *Input) { | ||
| if in.Image == "" { | ||
| // Note: mylocalton uses a compose file, not a single image. Reusing common image field | ||
| in.Image = "https://raw.githubusercontent.com/neodix42/mylocalton-docker/main/docker-compose.yaml" | ||
| } | ||
| // Note: in local env having all services could be useful(explorer, faucet), in CI we need only core services | ||
| if os.Getenv("CI") == "true" && len(in.TonCoreServices) == 0 { | ||
| // Note: mylocalton-docker's essential services, excluded explorer, restarter, faucet app, | ||
| in.TonCoreServices = []string{ | ||
| "genesis", "tonhttpapi", "event-cache", | ||
| "index-postgres", "index-worker", "index-api", | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func newTon(in *Input) (*Output, error) { | ||
| defaultTon(in) | ||
| containerName := framework.DefaultTCName("blockchain-node") | ||
|
|
||
| resp, err := http.Get(in.Image) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to download docker-compose file: %v", err) | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| tempDir, err := os.MkdirTemp(".", "ton-mylocalton-docker") | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create temp directory: %v", err) | ||
| } | ||
|
|
||
| defer func() { | ||
| // delete the folder whether it was successful or not | ||
| _ = os.RemoveAll(tempDir) | ||
| }() | ||
|
|
||
| composeFile := filepath.Join(tempDir, "docker-compose.yaml") | ||
| file, err := os.Create(composeFile) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create compose file: %v", err) | ||
| } | ||
|
|
||
| _, err = io.Copy(file, resp.Body) | ||
| if err != nil { | ||
| file.Close() | ||
| return nil, fmt.Errorf("failed to write compose file: %v", err) | ||
| } | ||
| file.Close() | ||
|
|
||
| ctx := context.Background() | ||
|
|
||
| var stack compose.ComposeStack | ||
| stack, err = compose.NewDockerComposeWith( | ||
| compose.WithStackFiles(composeFile), | ||
| compose.StackIdentifier(containerName), | ||
| ) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create compose stack: %v", err) | ||
| } | ||
|
|
||
| var upOpts []compose.StackUpOption | ||
| upOpts = append(upOpts, compose.Wait(true)) | ||
|
|
||
| if len(in.TonCoreServices) > 0 { | ||
| upOpts = append(upOpts, compose.RunServices(in.TonCoreServices...)) | ||
| } | ||
|
|
||
| // always wait for healthy | ||
| const genesisBlockID = "E7XwFSQzNkcRepUC23J2nRpASXpnsEKmyyHYV4u/FZY=" | ||
| execStrat := wait.ForExec([]string{ | ||
| "/usr/local/bin/lite-client", | ||
| "-a", "127.0.0.1:" + DefaultTonLiteServerPort, | ||
| "-b", genesisBlockID, | ||
| "-t", "3", | ||
| "-c", "last", | ||
| }). | ||
| WithPollInterval(5 * time.Second). | ||
| WithStartupTimeout(180 * time.Second) | ||
|
|
||
| stack = stack. | ||
| WaitForService("genesis", execStrat). | ||
| WaitForService("tonhttpapi", wait.ForListeningPort(DefaultTonHTTPAPIPort+"/tcp")) | ||
|
|
||
| if err := stack.Up(ctx, upOpts...); err != nil { | ||
| return nil, fmt.Errorf("failed to start compose stack: %w", err) | ||
| } | ||
|
|
||
| httpCtr, _ := stack.ServiceContainer(ctx, "genesis") | ||
| httpHost, _ := httpCtr.Host(ctx) | ||
| httpPort, _ := httpCtr.MappedPort(ctx, nat.Port(fmt.Sprintf("%s/tcp", DefaultTonSimpleServerPort))) | ||
skudasov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return &Output{ | ||
| UseCache: true, | ||
| ChainID: in.ChainID, | ||
| Type: in.Type, | ||
| Family: FamilyTon, | ||
| ContainerName: containerName, | ||
| // Note: in case we need 1+ validators, we need to modify the compose file | ||
| Nodes: []*Node{{ | ||
| // Note: define if we need more access other than the global config(tonutils-go only uses liteclients defined in the config) | ||
| ExternalHTTPUrl: fmt.Sprintf("%s:%s", httpHost, httpPort.Port()), | ||
| }}, | ||
| }, nil | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.