github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/docker/test_env/besu.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 "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/testcontext" 21 ) 22 23 type Besu struct { 24 EnvComponent 25 ExternalHttpUrl string 26 InternalHttpUrl string 27 ExternalWsUrl string 28 InternalWsUrl string 29 InternalExecutionURL string 30 ExternalExecutionURL string 31 generatedDataHostDir string 32 chainConfg *EthereumChainConfig 33 consensusLayer ConsensusLayer 34 l zerolog.Logger 35 t *testing.T 36 } 37 38 func NewBesu(networks []string, chainConfg *EthereumChainConfig, generatedDataHostDir string, consensusLayer ConsensusLayer, opts ...EnvComponentOption) (*Besu, error) { 39 // currently it uses 24.1 40 dockerImage, err := mirror.GetImage("hyperledger/besu:24") 41 42 if err != nil { 43 return nil, err 44 } 45 46 parts := strings.Split(dockerImage, ":") 47 g := &Besu{ 48 EnvComponent: EnvComponent{ 49 ContainerName: fmt.Sprintf("%s-%s", "besu", uuid.NewString()[0:8]), 50 Networks: networks, 51 ContainerImage: parts[0], 52 ContainerVersion: parts[1], 53 }, 54 chainConfg: chainConfg, 55 generatedDataHostDir: generatedDataHostDir, 56 consensusLayer: consensusLayer, 57 l: logging.GetTestLogger(nil), 58 } 59 g.SetDefaultHooks() 60 for _, opt := range opts { 61 opt(&g.EnvComponent) 62 } 63 return g, nil 64 } 65 66 func (g *Besu) WithTestInstance(t *testing.T) ExecutionClient { 67 g.l = logging.GetTestLogger(t) 68 g.t = t 69 return g 70 } 71 72 func (g *Besu) StartContainer() (blockchain.EVMNetwork, error) { 73 r, err := g.getContainerRequest(g.Networks) 74 if err != nil { 75 return blockchain.EVMNetwork{}, err 76 } 77 78 l := logging.GetTestContainersGoTestLogger(g.t) 79 ct, err := docker.StartContainerWithRetry(g.l, tc.GenericContainerRequest{ 80 ContainerRequest: *r, 81 Reuse: true, 82 Started: true, 83 Logger: l, 84 }) 85 if err != nil { 86 return blockchain.EVMNetwork{}, fmt.Errorf("cannot start Besu container: %w", err) 87 } 88 89 host, err := GetHost(testcontext.Get(g.t), ct) 90 if err != nil { 91 return blockchain.EVMNetwork{}, err 92 } 93 if err != nil { 94 return blockchain.EVMNetwork{}, err 95 } 96 httpPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(TX_GETH_HTTP_PORT)) 97 if err != nil { 98 return blockchain.EVMNetwork{}, err 99 } 100 wsPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(TX_GETH_WS_PORT)) 101 if err != nil { 102 return blockchain.EVMNetwork{}, err 103 } 104 executionPort, err := ct.MappedPort(testcontext.Get(g.t), NatPort(ETH2_EXECUTION_PORT)) 105 if err != nil { 106 return blockchain.EVMNetwork{}, err 107 } 108 109 g.Container = ct 110 g.ExternalHttpUrl = FormatHttpUrl(host, httpPort.Port()) 111 g.InternalHttpUrl = FormatHttpUrl(g.ContainerName, TX_GETH_HTTP_PORT) 112 g.ExternalWsUrl = FormatWsUrl(host, wsPort.Port()) 113 g.InternalWsUrl = FormatWsUrl(g.ContainerName, TX_GETH_WS_PORT) 114 g.InternalExecutionURL = FormatHttpUrl(g.ContainerName, ETH2_EXECUTION_PORT) 115 g.ExternalExecutionURL = FormatHttpUrl(host, executionPort.Port()) 116 117 networkConfig := blockchain.SimulatedEVMNetwork 118 networkConfig.Name = fmt.Sprintf("Simulated Eth2 (besu + %s)", g.consensusLayer) 119 networkConfig.URLs = []string{g.ExternalWsUrl} 120 networkConfig.HTTPURLs = []string{g.ExternalHttpUrl} 121 122 g.l.Info().Str("containerName", g.ContainerName). 123 Msg("Started Besu container") 124 125 return networkConfig, nil 126 } 127 128 func (g *Besu) GetInternalExecutionURL() string { 129 return g.InternalExecutionURL 130 } 131 132 func (g *Besu) GetExternalExecutionURL() string { 133 return g.ExternalExecutionURL 134 } 135 136 func (g *Besu) GetInternalHttpUrl() string { 137 return g.InternalHttpUrl 138 } 139 140 func (g *Besu) GetInternalWsUrl() string { 141 return g.InternalWsUrl 142 } 143 144 func (g *Besu) GetExternalHttpUrl() string { 145 return g.ExternalHttpUrl 146 } 147 148 func (g *Besu) GetExternalWsUrl() string { 149 return g.ExternalWsUrl 150 } 151 152 func (g *Besu) GetContainerName() string { 153 return g.ContainerName 154 } 155 156 func (g *Besu) GetContainer() *tc.Container { 157 return &g.Container 158 } 159 160 func (g *Besu) getContainerRequest(networks []string) (*tc.ContainerRequest, error) { 161 return &tc.ContainerRequest{ 162 Name: g.ContainerName, 163 Image: g.GetImageWithVersion(), 164 Networks: networks, 165 // ImagePlatform: "linux/x86_64", //don't even try this on Apple Silicon, the node won't start due to JVM error 166 ExposedPorts: []string{NatPortFormat(TX_GETH_HTTP_PORT), NatPortFormat(TX_GETH_WS_PORT), NatPortFormat(ETH2_EXECUTION_PORT)}, 167 WaitingFor: tcwait.ForAll( 168 tcwait.ForLog("Ethereum main loop is up"). 169 WithStartupTimeout(120 * time.Second). 170 WithPollInterval(1 * time.Second), 171 ), 172 User: "0:0", //otherwise in CI we get "permission denied" error, when trying to access data from mounted volume 173 Cmd: []string{ 174 "--data-path=/opt/besu/execution-data", 175 fmt.Sprintf("--genesis-file=%s/besu.json", GENERATED_DATA_DIR_INSIDE_CONTAINER), 176 fmt.Sprintf("--network-id=%d", g.chainConfg.ChainID), 177 "--host-allowlist=*", 178 "--rpc-http-enabled=true", 179 "--rpc-http-host=0.0.0.0", 180 fmt.Sprintf("--rpc-http-port=%s", TX_GETH_HTTP_PORT), 181 "--rpc-http-api=ADMIN,CLIQUE,ETH,NET,DEBUG,TXPOOL,ENGINE,TRACE,WEB3", 182 "--rpc-http-cors-origins=*", 183 "--rpc-ws-enabled=true", 184 "--rpc-ws-host=0.0.0.0", 185 fmt.Sprintf("--rpc-ws-port=%s", TX_GETH_WS_PORT), 186 "--rpc-ws-api=ADMIN,CLIQUE,ETH,NET,DEBUG,TXPOOL,ENGINE,TRACE,WEB3", 187 "--engine-rpc-enabled=true", 188 fmt.Sprintf("--engine-jwt-secret=%s", JWT_SECRET_FILE_LOCATION_INSIDE_CONTAINER), 189 "--engine-host-allowlist=*", 190 fmt.Sprintf("--engine-rpc-port=%s", ETH2_EXECUTION_PORT), 191 "--sync-mode=FULL", 192 "--data-storage-format=BONSAI", 193 // "--logging=DEBUG", 194 "--rpc-tx-feecap=0", 195 }, 196 Env: map[string]string{ 197 "JAVA_OPTS": "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n", 198 }, 199 Mounts: tc.ContainerMounts{ 200 tc.ContainerMount{ 201 Source: tc.GenericBindMountSource{ 202 HostPath: g.generatedDataHostDir, 203 }, 204 Target: tc.ContainerMountTarget(GENERATED_DATA_DIR_INSIDE_CONTAINER), 205 }, 206 }, 207 LifecycleHooks: []tc.ContainerLifecycleHooks{ 208 { 209 PostStarts: g.PostStartsHooks, 210 PostStops: g.PostStopsHooks, 211 }, 212 }, 213 }, nil 214 } 215 216 func (g *Besu) WaitUntilChainIsReady(ctx context.Context, waitTime time.Duration) error { 217 waitForFirstBlock := tcwait.NewLogStrategy("Imported #1").WithPollInterval(1 * time.Second).WithStartupTimeout(waitTime) 218 return waitForFirstBlock.WaitUntilReady(ctx, *g.GetContainer()) 219 } 220 221 func (g *Besu) GetContainerType() ContainerType { 222 return ContainerType_Besu 223 }