Skip to content

Commit 27b768e

Browse files
committed
wip: Sui support
1 parent a4290be commit 27b768e

File tree

8 files changed

+229
-1
lines changed

8 files changed

+229
-1
lines changed

framework/components/blockchain/blockchain.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
// Input is a blockchain network configuration params
99
type Input struct {
1010
// Common EVM fields
11-
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos" envconfig:"net_type"`
11+
Type string `toml:"type" validate:"required,oneof=anvil geth besu solana aptos sui" envconfig:"net_type"`
1212
Image string `toml:"image"`
1313
PullImage bool `toml:"pull_image"`
1414
Port string `toml:"port"`
@@ -33,11 +33,16 @@ type Output struct {
3333
UseCache bool `toml:"use_cache"`
3434
Family string `toml:"family"`
3535
ContainerName string `toml:"container_name"`
36+
GeneratedData *GeneratedData `toml:"generated_data"`
3637
Container testcontainers.Container `toml:"-"`
3738
ChainID string `toml:"chain_id"`
3839
Nodes []*Node `toml:"nodes"`
3940
}
4041

42+
type GeneratedData struct {
43+
Mnemonic string `toml:"mnemonic"`
44+
}
45+
4146
// Node represents blockchain node output, URLs required for connection locally and inside docker network
4247
type Node struct {
4348
HostWSUrl string `toml:"ws_url"`
@@ -64,6 +69,8 @@ func NewBlockchainNetwork(in *Input) (*Output, error) {
6469
out, err = newSolana(in)
6570
case "aptos":
6671
out, err = newAptos(in)
72+
case "sui":
73+
out, err = newSui(in)
6774
default:
6875
return nil, fmt.Errorf("blockchain type is not supported or empty, must be 'anvil' or 'geth'")
6976
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package blockchain
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/block-vision/sui-go-sdk/models"
8+
"github.com/docker/docker/api/types/container"
9+
"github.com/go-resty/resty/v2"
10+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
11+
"github.com/testcontainers/testcontainers-go"
12+
"github.com/testcontainers/testcontainers-go/wait"
13+
"path/filepath"
14+
"strings"
15+
"time"
16+
)
17+
18+
type SuiKeyInfo struct {
19+
Alias *string `json:"alias"` // Alias key name, usually "null"
20+
Flag int `json:"flag"` // Flag is an integer
21+
KeyScheme string `json:"keyScheme"` // Key scheme is a string
22+
Mnemonic string `json:"mnemonic"` // Mnemonic is a string
23+
PeerId string `json:"peerId"` // Peer ID is a string
24+
PublicBase64Key string `json:"publicBase64Key"` // Public key in Base64 format
25+
SuiAddress string `json:"suiAddress"` // Sui address is a 0x prefixed hex string
26+
}
27+
28+
func fundAccount(url string, address string) error {
29+
r := resty.New().SetBaseURL(url).EnableTrace().SetDebug(true)
30+
b := &models.FaucetRequest{
31+
FixedAmountRequest: &models.FaucetFixedAmountRequest{
32+
Recipient: address,
33+
},
34+
}
35+
resp, err := r.R().SetBody(b).SetHeader("Content-Type", "application/json").Post("/gas")
36+
if err != nil {
37+
return err
38+
}
39+
framework.L.Info().Any("Resp", resp).Msg("Address is funded!")
40+
return nil
41+
}
42+
43+
func generateKeyData(containerName string, keyCipherType string) (*SuiKeyInfo, error) {
44+
cmdStr := []string{"sui", "keytool", "generate", keyCipherType, "--json"}
45+
keyOut, err := framework.ExecContainer(containerName, cmdStr)
46+
if err != nil {
47+
return nil, err
48+
}
49+
cleanKey := strings.ReplaceAll(keyOut, "\x00", "")
50+
cleanKey = strings.ReplaceAll(cleanKey, "\x01", "")
51+
cleanKey = strings.ReplaceAll(cleanKey, "\x02", "")
52+
cleanKey = strings.ReplaceAll(cleanKey, "\n", "")
53+
var key *SuiKeyInfo
54+
if err := json.Unmarshal([]byte("{"+cleanKey[2:]), &key); err != nil {
55+
return nil, err
56+
}
57+
framework.L.Info().Interface("Key", key).Msg("Test key")
58+
return key, nil
59+
}
60+
61+
func defaultSui(in *Input) {
62+
if in.Image == "" {
63+
in.Image = "mysten/sui-tools:devnet"
64+
}
65+
if in.Port != "" {
66+
framework.L.Warn().Msg("'port' field is set but only default port can be used: 9000")
67+
}
68+
in.Port = "9000"
69+
}
70+
71+
func newSui(in *Input) (*Output, error) {
72+
defaultSui(in)
73+
ctx := context.Background()
74+
containerName := framework.DefaultTCName("blockchain-node")
75+
76+
absPath, err := filepath.Abs(in.ContractsDir)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
bindPort := fmt.Sprintf("%s/tcp", in.Port)
82+
83+
req := testcontainers.ContainerRequest{
84+
Image: in.Image,
85+
ExposedPorts: []string{in.Port, "9123/tcp"},
86+
Name: containerName,
87+
Labels: framework.DefaultTCLabels(),
88+
Networks: []string{framework.DefaultNetworkName},
89+
NetworkAliases: map[string][]string{
90+
framework.DefaultNetworkName: {containerName},
91+
},
92+
HostConfigModifier: func(h *container.HostConfig) {
93+
h.PortBindings = framework.MapTheSamePort(bindPort, "9123/tcp")
94+
},
95+
ImagePlatform: "linux/amd64",
96+
Env: map[string]string{
97+
"RUST_LOG": "off,sui_node=info",
98+
},
99+
Cmd: []string{
100+
"sui",
101+
"start",
102+
"--force-regenesis",
103+
"--with-faucet",
104+
},
105+
Files: []testcontainers.ContainerFile{
106+
{
107+
HostFilePath: absPath,
108+
ContainerFilePath: "/",
109+
},
110+
},
111+
// we need faucet for funding
112+
WaitingFor: wait.ForListeningPort("9123/tcp").WithStartupTimeout(10 * time.Second).WithPollInterval(200 * time.Millisecond),
113+
}
114+
115+
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
116+
ContainerRequest: req,
117+
Started: true,
118+
})
119+
if err != nil {
120+
return nil, err
121+
}
122+
host, err := c.Host(ctx)
123+
if err != nil {
124+
return nil, err
125+
}
126+
keyData, err := generateKeyData(containerName, "ed25519")
127+
if err != nil {
128+
return nil, err
129+
}
130+
err = fundAccount(fmt.Sprintf("http://%s:%s", "127.0.0.1", "9123"), keyData.SuiAddress)
131+
if err != nil {
132+
return nil, err
133+
}
134+
return &Output{
135+
UseCache: true,
136+
Family: "sui",
137+
ContainerName: containerName,
138+
GeneratedData: &GeneratedData{Mnemonic: keyData.Mnemonic},
139+
Nodes: []*Node{
140+
{
141+
HostHTTPUrl: fmt.Sprintf("http://%s:%s", host, in.Port),
142+
DockerInternalHTTPUrl: fmt.Sprintf("http://%s:%s", containerName, in.Port),
143+
},
144+
},
145+
}, nil
146+
}

framework/examples/myproject/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ replace (
1010
)
1111

1212
require (
13+
github.com/block-vision/sui-go-sdk v1.0.6
1314
github.com/blocto/solana-go-sdk v1.30.0
1415
github.com/ethereum/go-ethereum v1.14.11
1516
github.com/go-resty/resty/v2 v2.15.3
@@ -221,9 +222,13 @@ require (
221222
github.com/spf13/pflag v1.0.5 // indirect
222223
github.com/stretchr/objx v0.5.2 // indirect
223224
github.com/supranational/blst v0.3.13 // indirect
225+
github.com/tidwall/gjson v1.14.4 // indirect
226+
github.com/tidwall/match v1.1.1 // indirect
227+
github.com/tidwall/pretty v1.2.0 // indirect
224228
github.com/tklauser/go-sysconf v0.3.12 // indirect
225229
github.com/tklauser/numcpus v0.6.1 // indirect
226230
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
231+
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
227232
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
228233
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
229234
github.com/ugorji/go/codec v1.2.12 // indirect

framework/examples/myproject/go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
157157
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
158158
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
159159
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
160+
github.com/block-vision/sui-go-sdk v1.0.6 h1:FysCc4TJC8v4BEBbCjJPDR4iR5eKqJT1dxGwsT67etg=
161+
github.com/block-vision/sui-go-sdk v1.0.6/go.mod h1:FyK1vGE8lWm9QA1fdQpf1agfXQSMbPT8AV1BICgx6d8=
160162
github.com/blocto/solana-go-sdk v1.30.0 h1:GEh4GDjYk1lMhV/hqJDCyuDeCuc5dianbN33yxL88NU=
161163
github.com/blocto/solana-go-sdk v1.30.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs=
162164
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
@@ -899,7 +901,13 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70
899901
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
900902
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
901903
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
904+
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
905+
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
906+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
907+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
902908
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
909+
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
910+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
903911
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
904912
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
905913
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[blockchain_a]
2+
type = "sui"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package examples
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/block-vision/sui-go-sdk/models"
7+
"github.com/block-vision/sui-go-sdk/signer"
8+
"github.com/block-vision/sui-go-sdk/sui"
9+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
10+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
11+
"github.com/stretchr/testify/require"
12+
"testing"
13+
)
14+
15+
type CfgSui struct {
16+
BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"`
17+
}
18+
19+
func TestSuiSmoke(t *testing.T) {
20+
in, err := framework.Load[CfgSui](t)
21+
require.NoError(t, err)
22+
23+
bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA)
24+
require.NoError(t, err)
25+
26+
// execute any additional commands, to deploy contracts or set up
27+
// network is already funded, here are the keys
28+
_ = bc.GeneratedData.Mnemonic
29+
30+
_, err = framework.ExecContainer(bc.ContainerName, []string{"ls", "-lah"})
31+
require.NoError(t, err)
32+
33+
t.Run("test something", func(t *testing.T) {
34+
// use internal URL to connect Chainlink nodes
35+
_ = bc.Nodes[0].DockerInternalHTTPUrl
36+
// use host URL to interact
37+
_ = bc.Nodes[0].HostHTTPUrl
38+
cli := sui.NewSuiClient("http://localhost:9000")
39+
40+
signerAccount, err := signer.NewSignertWithMnemonic(bc.GeneratedData.Mnemonic)
41+
require.NoError(t, err)
42+
rsp, err := cli.SuiXGetAllBalance(context.Background(), models.SuiXGetAllBalanceRequest{
43+
Owner: signerAccount.Address,
44+
})
45+
require.NoError(t, err)
46+
fmt.Printf("My funds: %v\n", rsp)
47+
})
48+
}

framework/go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ require (
4848
github.com/aws/smithy-go v1.21.0 // indirect
4949
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
5050
github.com/bits-and-blooms/bitset v1.13.0 // indirect
51+
github.com/block-vision/sui-go-sdk v1.0.6 // indirect
5152
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
5253
github.com/bytedance/sonic v1.12.3 // indirect
5354
github.com/bytedance/sonic/loader v0.2.0 // indirect
@@ -132,6 +133,9 @@ require (
132133
github.com/shoenig/go-m1cpu v0.1.6 // indirect
133134
github.com/sirupsen/logrus v1.9.3 // indirect
134135
github.com/supranational/blst v0.3.13 // indirect
136+
github.com/tidwall/gjson v1.14.4 // indirect
137+
github.com/tidwall/match v1.1.1 // indirect
138+
github.com/tidwall/pretty v1.2.0 // indirect
135139
github.com/tklauser/go-sysconf v0.3.12 // indirect
136140
github.com/tklauser/numcpus v0.6.1 // indirect
137141
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect

framework/go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
5252
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
5353
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
5454
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
55+
github.com/block-vision/sui-go-sdk v1.0.6 h1:FysCc4TJC8v4BEBbCjJPDR4iR5eKqJT1dxGwsT67etg=
56+
github.com/block-vision/sui-go-sdk v1.0.6/go.mod h1:FyK1vGE8lWm9QA1fdQpf1agfXQSMbPT8AV1BICgx6d8=
5557
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
5658
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
5759
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
@@ -373,6 +375,12 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70
373375
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
374376
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
375377
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
378+
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
379+
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
380+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
381+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
382+
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
383+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
376384
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
377385
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
378386
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=

0 commit comments

Comments
 (0)