github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/erigon.go (about) 1 package test_env 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "html/template" 8 "os" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/google/uuid" 14 "github.com/rs/zerolog" 15 16 tc "github.com/testcontainers/testcontainers-go" 17 tcwait "github.com/testcontainers/testcontainers-go/wait" 18 19 "github.com/smartcontractkit/chainlink-testing-framework/libs/blockchain" 20 "github.com/smartcontractkit/chainlink-testing-framework/libs/docker" 21 "github.com/smartcontractkit/chainlink-testing-framework/libs/logging" 22 "github.com/smartcontractkit/chainlink-testing-framework/libs/mirror" 23 "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/testcontext" 24 ) 25 26 type Erigon struct { 27 EnvComponent 28 ExternalHttpUrl string 29 InternalHttpUrl string 30 ExternalWsUrl string 31 InternalWsUrl string 32 InternalExecutionURL string 33 ExternalExecutionURL string 34 generatedDataHostDir string 35 chainConfg *EthereumChainConfig 36 consensusLayer ConsensusLayer 37 l zerolog.Logger 38 t *testing.T 39 } 40 41 func NewErigon(networks []string, chainConfg *EthereumChainConfig, generatedDataHostDir string, consensusLayer ConsensusLayer, opts ...EnvComponentOption) (*Erigon, error) { 42 // currently it uses v2.56.0 43 dockerImage, err := mirror.GetImage("thorax/erigon:v2.56") 44 if err != nil { 45 return nil, err 46 } 47 48 parts := strings.Split(dockerImage, ":") 49 g := &Erigon{ 50 EnvComponent: EnvComponent{ 51 ContainerName: fmt.Sprintf("%s-%s", "erigon", uuid.NewString()[0:8]), 52 Networks: networks, 53 ContainerImage: parts[0], 54 ContainerVersion: parts[1], 55 }, 56 chainConfg: chainConfg, 57 generatedDataHostDir: generatedDataHostDir, 58 consensusLayer: consensusLayer, 59 l: logging.GetTestLogger(nil), 60 } 61 g.SetDefaultHooks() 62 for _, opt := range opts { 63 opt(&g.EnvComponent) 64 } 65 return g, nil 66 } 67 68 func (g *Erigon) WithTestInstance(t *testing.T) ExecutionClient { 69 g.l = logging.GetTestLogger(t) 70 g.t = t 71 return g 72 } 73 74 func (g *Erigon) StartContainer() (blockchain.EVMNetwork, error) { 75 r, err := g.getContainerRequest(g.Networks) 76 if err != nil { 77 return blockchain.EVMNetwork{}, err 78 } 79 80 l := logging.GetTestContainersGoTestLogger(g.t) 81 ct, err := docker.StartContainerWithRetry(g.l, tc.GenericContainerRequest{ 82 ContainerRequest: *r, 83 Reuse: true, 84 Started: true, 85 Logger: l, 86 }) 87 if err != nil { 88 return blockchain.EVMNetwork{}, fmt.Errorf("cannot start erigon container: %w", err) 89 } 90 91 host, err := GetHost(testcontext.Get(g.t), ct) 92 if err != nil { 93 return blockchain.EVMNetwork{}, err 94 } 95 if err != nil { 96 return blockchain.EVMNetwork{}, err 97 } 98 httpPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(TX_GETH_HTTP_PORT)) 99 if err != nil { 100 return blockchain.EVMNetwork{}, err 101 } 102 executionPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(ETH2_EXECUTION_PORT)) 103 if err != nil { 104 return blockchain.EVMNetwork{}, err 105 } 106 107 g.Container = ct 108 g.ExternalHttpUrl = FormatHttpUrl(host, httpPort.Port()) 109 g.InternalHttpUrl = FormatHttpUrl(g.ContainerName, TX_GETH_HTTP_PORT) 110 g.ExternalWsUrl = FormatWsUrl(host, httpPort.Port()) 111 g.InternalWsUrl = FormatWsUrl(g.ContainerName, TX_GETH_HTTP_PORT) 112 g.InternalExecutionURL = FormatHttpUrl(g.ContainerName, ETH2_EXECUTION_PORT) 113 g.ExternalExecutionURL = FormatHttpUrl(host, executionPort.Port()) 114 115 networkConfig := blockchain.SimulatedEVMNetwork 116 networkConfig.Name = fmt.Sprintf("Simulated Eth2 (erigon + %s)", g.consensusLayer) 117 networkConfig.URLs = []string{g.ExternalWsUrl} 118 networkConfig.HTTPURLs = []string{g.ExternalHttpUrl} 119 120 g.l.Info().Str("containerName", g.ContainerName). 121 Msg("Started Erigon container") 122 123 return networkConfig, nil 124 } 125 126 func (g *Erigon) GetInternalExecutionURL() string { 127 return g.InternalExecutionURL 128 } 129 130 func (g *Erigon) GetExternalExecutionURL() string { 131 return g.ExternalExecutionURL 132 } 133 134 func (g *Erigon) GetInternalHttpUrl() string { 135 return g.InternalHttpUrl 136 } 137 138 func (g *Erigon) GetInternalWsUrl() string { 139 return g.InternalWsUrl 140 } 141 142 func (g *Erigon) GetExternalHttpUrl() string { 143 return g.ExternalHttpUrl 144 } 145 146 func (g *Erigon) GetExternalWsUrl() string { 147 return g.ExternalWsUrl 148 } 149 150 func (g *Erigon) GetContainerName() string { 151 return g.ContainerName 152 } 153 154 func (g *Erigon) GetContainer() *tc.Container { 155 return &g.Container 156 } 157 158 func (g *Erigon) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { 159 initFile, err := os.CreateTemp("", "init.sh") 160 if err != nil { 161 return nil, err 162 } 163 164 initScriptContent, err := g.buildInitScript() 165 if err != nil { 166 return nil, err 167 } 168 169 _, err = initFile.WriteString(initScriptContent) 170 if err != nil { 171 return nil, err 172 } 173 174 return &tc.ContainerRequest{ 175 Name: g.ContainerName, 176 Image: g.GetImageWithVersion(), 177 Networks: networks, 178 ImagePlatform: "linux/x86_64", 179 ExposedPorts: []string{NatPortFormat(TX_GETH_HTTP_PORT), NatPortFormat(ETH2_EXECUTION_PORT)}, 180 WaitingFor: tcwait.ForAll( 181 tcwait.ForLog("Started P2P networking"). 182 WithStartupTimeout(120 * time.Second). 183 WithPollInterval(1 * time.Second), 184 ), 185 User: "0:0", 186 Entrypoint: []string{ 187 "sh", 188 "/home/erigon/init.sh", 189 }, 190 Files: []tc.ContainerFile{ 191 { 192 HostFilePath: initFile.Name(), 193 ContainerFilePath: "/home/erigon/init.sh", 194 FileMode: 0744, 195 }, 196 }, 197 Mounts: tc.ContainerMounts{ 198 tc.ContainerMount{ 199 Source: tc.GenericBindMountSource{ 200 HostPath: g.generatedDataHostDir, 201 }, 202 Target: tc.ContainerMountTarget(GENERATED_DATA_DIR_INSIDE_CONTAINER), 203 }, 204 }, 205 LifecycleHooks: []tc.ContainerLifecycleHooks{ 206 { 207 PostStarts: g.PostStartsHooks, 208 PostStops: g.PostStopsHooks, 209 }, 210 }, 211 }, nil 212 } 213 214 func (g *Erigon) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { 215 waitForFirstBlock := tcwait.NewLogStrategy("Built block").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) 216 return waitForFirstBlock.WaitUntilReady(ctx, *g.GetContainer()) 217 } 218 219 func (g *Erigon) buildInitScript() (string, error) { 220 initTemplate := `#!/bin/bash 221 echo "Copied genesis file to {{.ExecutionDir}}" 222 mkdir -p {{.ExecutionDir}} 223 cp {{.GeneratedDataDir}}/genesis.json {{.ExecutionDir}}/genesis.json 224 echo "Running erigon init" 225 erigon init --datadir={{.ExecutionDir}} {{.ExecutionDir}}/genesis.json 226 exit_code=$? 227 if [ $exit_code -ne 0 ]; then 228 echo "Erigon init failed with exit code $exit_code" 229 exit 1 230 fi 231 232 echo "Starting Erigon..." 233 erigon --http --http.api=eth,erigon,engine,web3,net,debug,trace,txpool,admin --http.addr=0.0.0.0 --http.corsdomain=* \ 234 --http.vhosts=* --http.port={{.HttpPort}} --ws --authrpc.vhosts=* --authrpc.addr=0.0.0.0 --authrpc.jwtsecret={{.JwtFileLocation}} \ 235 --datadir={{.ExecutionDir}} --rpc.allow-unprotected-txs --rpc.txfeecap=0 --allow-insecure-unlock \ 236 --nodiscover --networkid={{.ChainID}} --db.size.limit=8TB` 237 238 data := struct { 239 HttpPort string 240 ChainID int 241 GeneratedDataDir string 242 JwtFileLocation string 243 ExecutionDir string 244 }{ 245 HttpPort: TX_GETH_HTTP_PORT, 246 ChainID: g.chainConfg.ChainID, 247 GeneratedDataDir: GENERATED_DATA_DIR_INSIDE_CONTAINER, 248 JwtFileLocation: JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER, 249 ExecutionDir: "/home/erigon/execution-data", 250 } 251 252 t, err := template.New("init").Parse(initTemplate) 253 if err != nil { 254 fmt.Println("Error parsing template:", err) 255 os.Exit(1) 256 } 257 258 var buf bytes.Buffer 259 err = t.Execute(&buf, data) 260 261 return buf.String(), err 262 263 } 264 265 func (g *Erigon) GetContainerType() ContainerType { 266 return ContainerType_Erigon 267 }