github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/nethermind.go (about) 1 package test_env 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/google/uuid" 11 "github.com/rs/zerolog" 12 13 tc "github.com/testcontainers/testcontainers-go" 14 tcwait "github.com/testcontainers/testcontainers-go/wait" 15 16 "github.com/smartcontractkit/chainlink-testing-framework/libs/blockchain" 17 "github.com/smartcontractkit/chainlink-testing-framework/libs/docker" 18 "github.com/smartcontractkit/chainlink-testing-framework/libs/logging" 19 "github.com/smartcontractkit/chainlink-testing-framework/libs/mirror" 20 ) 21 22 type Nethermind struct { 23 EnvComponent 24 ExternalHttpUrl string 25 InternalHttpUrl string 26 ExternalWsUrl string 27 InternalWsUrl string 28 InternalExecutionURL string 29 ExternalExecutionURL string 30 generatedDataHostDir string 31 consensusLayer ConsensusLayer 32 l zerolog.Logger 33 t *testing.T 34 } 35 36 func NewNethermind(networks []string, generatedDataHostDir string, consensusLayer ConsensusLayer, opts ...EnvComponentOption) (*Nethermind, error) { 37 // currently it uses 1.25.1 38 dockerImage, err := mirror.GetImage("nethermind/nethermind:1") 39 if err != nil { 40 return nil, err 41 } 42 43 parts := strings.Split(dockerImage, ":") 44 g := &Nethermind{ 45 EnvComponent: EnvComponent{ 46 ContainerName: fmt.Sprintf("%s-%s", "nethermind", uuid.NewString()[0:8]), 47 Networks: networks, 48 ContainerImage: parts[0], 49 ContainerVersion: parts[1], 50 }, 51 generatedDataHostDir: generatedDataHostDir, 52 consensusLayer: consensusLayer, 53 l: logging.GetTestLogger(nil), 54 } 55 for _, opt := range opts { 56 opt(&g.EnvComponent) 57 } 58 return g, nil 59 } 60 61 func (g *Nethermind) WithTestInstance(t *testing.T) ExecutionClient { 62 g.l = logging.GetTestLogger(t) 63 g.t = t 64 return g 65 } 66 67 func (g *Nethermind) StartContainer() (blockchain.EVMNetwork, error) { 68 r, err := g.getContainerRequest(g.Networks) 69 if err != nil { 70 return blockchain.EVMNetwork{}, err 71 } 72 73 l := logging.GetTestContainersGoTestLogger(g.t) 74 ct, err := docker.StartContainerWithRetry(g.l, tc.GenericContainerRequest{ 75 ContainerRequest: *r, 76 Reuse: true, 77 Started: true, 78 Logger: l, 79 }) 80 if err != nil { 81 return blockchain.EVMNetwork{}, fmt.Errorf("cannot start nethermind container: %w", err) 82 } 83 84 host, err := GetHost(context.Background(), ct) 85 if err != nil { 86 return blockchain.EVMNetwork{}, err 87 } 88 if err != nil { 89 return blockchain.EVMNetwork{}, err 90 } 91 httpPort, err := ct.MappedPort(context.Background(), NatPort(TX_GETH_HTTP_PORT)) 92 if err != nil { 93 return blockchain.EVMNetwork{}, err 94 } 95 wsPort, err := ct.MappedPort(context.Background(), NatPort(TX_GETH_WS_PORT)) 96 if err != nil { 97 return blockchain.EVMNetwork{}, err 98 } 99 executionPort, err := ct.MappedPort(context.Background(), NatPort(ETH2_EXECUTION_PORT)) 100 if err != nil { 101 return blockchain.EVMNetwork{}, err 102 } 103 104 g.Container = ct 105 g.ExternalHttpUrl = FormatHttpUrl(host, httpPort.Port()) 106 g.InternalHttpUrl = FormatHttpUrl(g.ContainerName, TX_GETH_HTTP_PORT) 107 g.ExternalWsUrl = FormatWsUrl(host, wsPort.Port()) 108 g.InternalWsUrl = FormatWsUrl(g.ContainerName, TX_GETH_WS_PORT) 109 g.InternalExecutionURL = FormatHttpUrl(g.ContainerName, ETH2_EXECUTION_PORT) 110 g.ExternalExecutionURL = FormatHttpUrl(host, executionPort.Port()) 111 112 networkConfig := blockchain.SimulatedEVMNetwork 113 networkConfig.Name = fmt.Sprintf("Simulated Eth2 (nethermind + %s)", g.consensusLayer) 114 networkConfig.URLs = []string{g.ExternalWsUrl} 115 networkConfig.HTTPURLs = []string{g.ExternalHttpUrl} 116 117 g.l.Info().Str("containerName", g.ContainerName). 118 Msg("Started Nethermind container") 119 120 return networkConfig, nil 121 } 122 123 func (g *Nethermind) GetInternalExecutionURL() string { 124 return g.InternalExecutionURL 125 } 126 127 func (g *Nethermind) GetExternalExecutionURL() string { 128 return g.ExternalExecutionURL 129 } 130 131 func (g *Nethermind) GetInternalHttpUrl() string { 132 return g.InternalHttpUrl 133 } 134 135 func (g *Nethermind) GetInternalWsUrl() string { 136 return g.InternalWsUrl 137 } 138 139 func (g *Nethermind) GetExternalHttpUrl() string { 140 return g.ExternalHttpUrl 141 } 142 143 func (g *Nethermind) GetExternalWsUrl() string { 144 return g.ExternalWsUrl 145 } 146 147 func (g *Nethermind) GetContainerName() string { 148 return g.ContainerName 149 } 150 151 func (g *Nethermind) GetContainer() *tc.Container { 152 return &g.Container 153 } 154 155 func (g *Nethermind) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { 156 return &tc.ContainerRequest{ 157 Name: g.ContainerName, 158 Image: g.GetImageWithVersion(), 159 Networks: networks, 160 AlwaysPullImage: true, 161 // ImagePlatform: "linux/x86_64", //don't even try this on Apple Silicon, the node won't start due to .NET error 162 ExposedPorts: []string{NatPortFormat(TX_GETH_HTTP_PORT), NatPortFormat(TX_GETH_WS_PORT), NatPortFormat(ETH2_EXECUTION_PORT)}, 163 WaitingFor: tcwait.ForAll( 164 tcwait.ForLog("Nethermind initialization completed"). 165 WithStartupTimeout(120 * time.Second). 166 WithPollInterval(1 * time.Second), 167 ), 168 Cmd: []string{ 169 "--datadir=/nethermind", 170 "--config=none.cfg", 171 fmt.Sprintf("--Init.ChainSpecPath=%s/chainspec.json", GENERATED_DATA_DIR_INSIDE_CONTAINER), 172 "--Init.DiscoveryEnabled=false", 173 "--Init.WebSocketsEnabled=true", 174 fmt.Sprintf("--JsonRpc.WebSocketsPort=%s", TX_GETH_WS_PORT), 175 "--JsonRpc.Enabled=true", 176 "--JsonRpc.EnabledModules=net,eth,consensus,subscribe,web3,admin", 177 "--JsonRpc.Host=0.0.0.0", 178 fmt.Sprintf("--JsonRpc.Port=%s", TX_GETH_HTTP_PORT), 179 "--JsonRpc.EngineHost=0.0.0.0", 180 "--JsonRpc.EnginePort=" + ETH2_EXECUTION_PORT, 181 fmt.Sprintf("--JsonRpc.JwtSecretFile=%s", JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER), 182 fmt.Sprintf("--KeyStore.KeyStoreDirectory=%s", KEYSTORE_DIR_LOCATION_INSIDE_CONTAINER), 183 "--KeyStore.BlockAuthorAccount=0x123463a4b065722e99115d6c222f267d9cabb524", 184 "--KeyStore.UnlockAccounts=0x123463a4b065722e99115d6c222f267d9cabb524", 185 fmt.Sprintf("--KeyStore.PasswordFiles=%s", ACCOUNT_PASSWORD_FILE_INSIDE_CONTAINER), 186 "--Network.MaxActivePeers=0", 187 "--Network.OnlyStaticPeers=true", 188 "--HealthChecks.Enabled=true", // default slug /health 189 }, 190 Mounts: tc.ContainerMounts{ 191 tc.ContainerMount{ 192 Source: tc.GenericBindMountSource{ 193 HostPath: g.generatedDataHostDir, 194 }, 195 Target: tc.ContainerMountTarget(GENERATED_DATA_DIR_INSIDE_CONTAINER), 196 }, 197 }, 198 LifecycleHooks: []tc.ContainerLifecycleHooks{ 199 { 200 PostStarts: g.PostStartsHooks, 201 PostStops: g.PostStopsHooks, 202 }, 203 }, 204 }, nil 205 } 206 207 func (g *Nethermind) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { 208 waitForFirstBlock := tcwait.NewLogStrategy("Improved post-merge block").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) 209 return waitForFirstBlock.WaitUntilReady(ctx, *g.GetContainer()) 210 } 211 212 func (g *Nethermind) GetContainerType() ContainerType { 213 return ContainerType_Nethermind 214 }