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  }