diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6d5b0c..18b83b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,5 +15,5 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.21.10 + go-version: 1.22.7 - run: cd ${GITHUB_WORKSPACE} && go build . diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5a79a69..b8ce61f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.21.10 + go-version: 1.22.7 - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 152dc04..1cdac1b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.21.10 + go-version: 1.22.7 - name: Run tests env: diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..cdda340 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,5 @@ +issues: + exclude-rules: + - linters: + - staticcheck + text: "ST1005" \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 5240368..7b461d6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,9 @@ "program": "${workspaceFolder}", "args": [ "-u", - "${env:HOME}/.hyperdrive" + "${env:HOME}/.hyperdrive", + "-s", + "/usr/share/hyperdrive/networks" ] } ] diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f4d686..0dccf4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,12 @@ }, "go.testFlags": [ "-p", + "1", + "-count", "1" + ], + "go.lintFlags": [ + "-e", + "ST1005" ] } \ No newline at end of file diff --git a/client/client.go b/client/client.go index 1cfb2d6..897885f 100644 --- a/client/client.go +++ b/client/client.go @@ -10,13 +10,14 @@ import ( // Binder for the Hyperdrive daemon API server type ApiClient struct { - context client.IRequesterContext - NodeSet *NodeSetRequester - NodeSet_StakeWise *NodeSetStakeWiseRequester - Service *ServiceRequester - Tx *TxRequester - Utils *UtilsRequester - Wallet *WalletRequester + context client.IRequesterContext + NodeSet *NodeSetRequester + NodeSet_StakeWise *NodeSetStakeWiseRequester + NodeSet_Constellation *NodeSetConstellationRequester + Service *ServiceRequester + Tx *TxRequester + Utils *UtilsRequester + Wallet *WalletRequester } // Creates a new API client instance @@ -24,13 +25,14 @@ func NewApiClient(apiUrl *url.URL, logger *slog.Logger, tracer *httptrace.Client context := client.NewNetworkRequesterContext(apiUrl, logger, tracer) client := &ApiClient{ - context: context, - NodeSet: NewNodeSetRequester(context), - NodeSet_StakeWise: NewNodeSetStakeWiseRequester(context), - Service: NewServiceRequester(context), - Tx: NewTxRequester(context), - Utils: NewUtilsRequester(context), - Wallet: NewWalletRequester(context), + context: context, + NodeSet: NewNodeSetRequester(context), + NodeSet_StakeWise: NewNodeSetStakeWiseRequester(context), + NodeSet_Constellation: NewNodeSetConstellationRequester(context), + Service: NewServiceRequester(context), + Tx: NewTxRequester(context), + Utils: NewUtilsRequester(context), + Wallet: NewWalletRequester(context), } return client } diff --git a/client/nodeset-constellation.go b/client/nodeset-constellation.go new file mode 100644 index 0000000..6593808 --- /dev/null +++ b/client/nodeset-constellation.go @@ -0,0 +1,67 @@ +package client + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + nscommon "github.com/nodeset-org/nodeset-client-go/common" + "github.com/rocket-pool/node-manager-core/api/client" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// Requester for Constellation module calls to the nodeset.io service +type NodeSetConstellationRequester struct { + context client.IRequesterContext +} + +func NewNodeSetConstellationRequester(context client.IRequesterContext) *NodeSetConstellationRequester { + return &NodeSetConstellationRequester{ + context: context, + } +} + +func (r *NodeSetConstellationRequester) GetName() string { + return "NodeSet-Constellation" +} +func (r *NodeSetConstellationRequester) GetRoute() string { + return "nodeset/constellation" +} +func (r *NodeSetConstellationRequester) GetContext() client.IRequesterContext { + return r.context +} + +// Gets the address the node's user has assigned as the registered Constellation address +func (r *NodeSetConstellationRequester) GetRegisteredAddress() (*types.ApiResponse[api.NodeSetConstellation_GetRegisteredAddressData], error) { + args := map[string]string{} + return client.SendGetRequest[api.NodeSetConstellation_GetRegisteredAddressData](r, "get-registered-address", "GetRegisteredAddress", args) +} + +// Gets a signature for registering / whitelisting the node with the Constellation contracts +func (r *NodeSetConstellationRequester) GetRegistrationSignature() (*types.ApiResponse[api.NodeSetConstellation_GetRegistrationSignatureData], error) { + args := map[string]string{} + return client.SendGetRequest[api.NodeSetConstellation_GetRegistrationSignatureData](r, "get-registration-signature", "GetRegistrationSignature", args) +} + +// Gets the deposit signature for a minipool from the Constellation contracts +func (r *NodeSetConstellationRequester) GetDepositSignature(minipoolAddress common.Address, salt *big.Int) (*types.ApiResponse[api.NodeSetConstellation_GetDepositSignatureData], error) { + args := map[string]string{ + "minipoolAddress": minipoolAddress.Hex(), + "salt": salt.String(), + } + return client.SendGetRequest[api.NodeSetConstellation_GetDepositSignatureData](r, "get-deposit-signature", "GetDepositSignature", args) +} + +// Gets the validators that have been registered with the NodeSet service for this node as part of Constellation +func (r *NodeSetConstellationRequester) GetValidators() (*types.ApiResponse[api.NodeSetConstellation_GetValidatorsData], error) { + args := map[string]string{} + return client.SendGetRequest[api.NodeSetConstellation_GetValidatorsData](r, "get-validators", "GetValidators", args) +} + +// Uploads signed exit messages to the NodeSet service +func (r *NodeSetConstellationRequester) UploadSignedExits(exitMessages []nscommon.ExitData) (*types.ApiResponse[api.NodeSetConstellation_UploadSignedExitsData], error) { + body := api.NodeSetConstellation_UploadSignedExitsRequestBody{ + ExitMessages: exitMessages, + } + return client.SendPostRequest[api.NodeSetConstellation_UploadSignedExitsData](r, "upload-signed-exits", "UploadSignedExits", body) +} diff --git a/client/nodeset-stakewise.go b/client/nodeset-stakewise.go index 83b0e8e..b676374 100644 --- a/client/nodeset-stakewise.go +++ b/client/nodeset-stakewise.go @@ -3,7 +3,7 @@ package client import ( "github.com/ethereum/go-ethereum/common" "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" - apiv1 "github.com/nodeset-org/nodeset-client-go/api-v1" + nscommon "github.com/nodeset-org/nodeset-client-go/common" "github.com/rocket-pool/node-manager-core/api/client" "github.com/rocket-pool/node-manager-core/api/types" "github.com/rocket-pool/node-manager-core/beacon" @@ -55,11 +55,19 @@ func (r *NodeSetStakeWiseRequester) GetDepositDataSet(vault common.Address) (*ty } // Uploads new validator deposit data to the NodeSet service -func (r *NodeSetStakeWiseRequester) UploadDepositData(data []beacon.ExtendedDepositData) (*types.ApiResponse[api.NodeSetStakeWise_UploadDepositDataData], error) { - return client.SendPostRequest[api.NodeSetStakeWise_UploadDepositDataData](r, "upload-deposit-data", "UploadDepositData", data) +func (r *NodeSetStakeWiseRequester) UploadDepositData(vault common.Address, data []beacon.ExtendedDepositData) (*types.ApiResponse[api.NodeSetStakeWise_UploadDepositDataData], error) { + body := api.NodeSetStakeWise_UploadDepositDataRequestBody{ + Vault: vault, + DepositData: data, + } + return client.SendPostRequest[api.NodeSetStakeWise_UploadDepositDataData](r, "upload-deposit-data", "UploadDepositData", body) } // Uploads signed exit messages to the NodeSet service -func (r *NodeSetStakeWiseRequester) UploadSignedExits(data []apiv1.ExitData) (*types.ApiResponse[api.NodeSetStakeWise_UploadSignedExitsData], error) { - return client.SendPostRequest[api.NodeSetStakeWise_UploadSignedExitsData](r, "upload-signed-exits", "UploadSignedExits", data) +func (r *NodeSetStakeWiseRequester) UploadSignedExits(vault common.Address, data []nscommon.ExitData) (*types.ApiResponse[api.NodeSetStakeWise_UploadSignedExitsData], error) { + body := api.NodeSetStakeWise_UploadSignedExitsRequestBody{ + Vault: vault, + ExitData: data, + } + return client.SendPostRequest[api.NodeSetStakeWise_UploadSignedExitsData](r, "upload-signed-exits", "UploadSignedExits", body) } diff --git a/client/service.go b/client/service.go index 04da773..633da70 100644 --- a/client/service.go +++ b/client/service.go @@ -31,6 +31,16 @@ func (r *ServiceRequester) ClientStatus() (*types.ApiResponse[api.ServiceClientS return client.SendGetRequest[api.ServiceClientStatusData](r, "client-status", "ClientStatus", nil) } +// Gets the resources for the daemon's selected network +func (r *ServiceRequester) GetResources() (*types.ApiResponse[api.ServiceGetResourcesData], error) { + return client.SendGetRequest[api.ServiceGetResourcesData](r, "get-resources", "GetResources", nil) +} + +// Gets the network settings for the daemon's selected network +func (r *ServiceRequester) GetNetworkSettings() (*types.ApiResponse[api.ServiceGetNetworkSettingsData], error) { + return client.SendGetRequest[api.ServiceGetNetworkSettingsData](r, "get-network-settings", "GetNetworkSettings", nil) +} + // Gets the Hyperdrive configuration func (r *ServiceRequester) GetConfig() (*types.ApiResponse[api.ServiceGetConfigData], error) { return client.SendGetRequest[api.ServiceGetConfigData](r, "get-config", "GetConfig", nil) diff --git a/common/nodeset-manager.go b/common/nodeset-manager.go index 088e8f9..a3771b4 100644 --- a/common/nodeset-manager.go +++ b/common/nodeset-manager.go @@ -5,16 +5,21 @@ import ( "errors" "fmt" "log/slog" + "math/big" "sync" "github.com/ethereum/go-ethereum/common" - "github.com/nodeset-org/hyperdrive-daemon/module-utils/services" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" - apiv1 "github.com/nodeset-org/nodeset-client-go/api-v1" + apiv2 "github.com/nodeset-org/nodeset-client-go/api-v2" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + nscommon "github.com/nodeset-org/nodeset-client-go/common" + "github.com/nodeset-org/nodeset-client-go/common/core" + "github.com/nodeset-org/nodeset-client-go/common/stakewise" "github.com/rocket-pool/node-manager-core/beacon" "github.com/rocket-pool/node-manager-core/log" "github.com/rocket-pool/node-manager-core/node/wallet" + "github.com/rocket-pool/node-manager-core/utils" ) // NodeSetServiceManager is a manager for interactions with the NodeSet service @@ -23,10 +28,10 @@ type NodeSetServiceManager struct { wallet *wallet.Wallet // Resources for the current network - resources *hdconfig.HyperdriveResources + resources *hdconfig.MergedResources - // Client for the v1 API - v1Client *apiv1.NodeSetClient + // Client for the v2 API + v2Client *apiv2.NodeSetClient // The current session token sessionToken string @@ -39,14 +44,14 @@ type NodeSetServiceManager struct { } // Creates a new NodeSet service manager -func NewNodeSetServiceManager(sp *ServiceProvider) *NodeSetServiceManager { +func NewNodeSetServiceManager(sp IHyperdriveServiceProvider) *NodeSetServiceManager { wallet := sp.GetWallet() resources := sp.GetResources() return &NodeSetServiceManager{ wallet: wallet, resources: resources, - v1Client: apiv1.NewNodeSetClient(resources.NodeSetApiUrl, hdconfig.ClientTimeout), + v2Client: apiv2.NewNodeSetClient(resources.NodeSetApiUrl, hdconfig.ClientTimeout), nodeRegistrationStatus: api.NodeSetRegistrationStatus_Unknown, lock: &sync.Mutex{}, } @@ -105,21 +110,13 @@ func (m *NodeSetServiceManager) RegisterNode(ctx context.Context, email string) return RegistrationResult_Unknown, fmt.Errorf("can't register node with NodeSet, wallet not loaded") } - // Create the signature - message := fmt.Sprintf(apiv1.NodeAddressMessageFormat, email, walletStatus.Wallet.WalletAddress.Hex()) - sigBytes, err := m.wallet.SignMessage([]byte(message)) - if err != nil { - m.setRegistrationStatus(api.NodeSetRegistrationStatus_Unknown) - return RegistrationResult_Unknown, fmt.Errorf("error signing registration message: %w", err) - } - // Run the request - err = m.v1Client.NodeAddress(ctx, email, walletStatus.Wallet.WalletAddress, sigBytes) + err = m.v2Client.Core.NodeAddress(ctx, email, walletStatus.Wallet.WalletAddress, m.wallet.SignMessage) if err != nil { m.setRegistrationStatus(api.NodeSetRegistrationStatus_Unknown) - if errors.Is(err, apiv1.ErrAlreadyRegistered) { + if errors.Is(err, core.ErrAlreadyRegistered) { return RegistrationResult_AlreadyRegistered, nil - } else if errors.Is(err, apiv1.ErrNotWhitelisted) { + } else if errors.Is(err, core.ErrNotWhitelisted) { return RegistrationResult_NotWhitelisted, nil } return RegistrationResult_Unknown, fmt.Errorf("error registering node: %w", err) @@ -127,6 +124,10 @@ func (m *NodeSetServiceManager) RegisterNode(ctx context.Context, email string) return RegistrationResult_Success, nil } +// ========================= +// === StakeWise Methods === +// ========================= + // Get the version of the latest deposit data set from the server func (m *NodeSetServiceManager) StakeWise_GetServerDepositDataVersion(ctx context.Context, vault common.Address) (int, error) { m.lock.Lock() @@ -140,10 +141,10 @@ func (m *NodeSetServiceManager) StakeWise_GetServerDepositDataVersion(ctx contex logger.Debug("Getting server deposit data version") // Run the request - var data apiv1.DepositDataMetaData + var data stakewise.DepositDataMetaData err := m.runRequest(ctx, func(ctx context.Context) error { var err error - data, err = m.v1Client.DepositDataMeta(ctx, vault, m.resources.EthNetworkName) + data, err = m.v2Client.StakeWise.DepositDataMeta(ctx, m.resources.DeploymentName, vault) return err }) if err != nil { @@ -165,10 +166,10 @@ func (m *NodeSetServiceManager) StakeWise_GetServerDepositData(ctx context.Conte logger.Debug("Getting deposit data") // Run the request - var data apiv1.DepositDataData + var data stakewise.DepositDataData err := m.runRequest(ctx, func(ctx context.Context) error { var err error - data, err = m.v1Client.DepositData_Get(ctx, vault, m.resources.EthNetworkName) + data, err = m.v2Client.StakeWise.DepositData_Get(ctx, m.resources.DeploymentName, vault) return err }) if err != nil { @@ -178,7 +179,7 @@ func (m *NodeSetServiceManager) StakeWise_GetServerDepositData(ctx context.Conte } // Uploads local deposit data set to the server -func (m *NodeSetServiceManager) StakeWise_UploadDepositData(ctx context.Context, depositData []beacon.ExtendedDepositData) error { +func (m *NodeSetServiceManager) StakeWise_UploadDepositData(ctx context.Context, vault common.Address, depositData []beacon.ExtendedDepositData) error { m.lock.Lock() defer m.lock.Unlock() @@ -191,7 +192,7 @@ func (m *NodeSetServiceManager) StakeWise_UploadDepositData(ctx context.Context, // Run the request err := m.runRequest(ctx, func(ctx context.Context) error { - return m.v1Client.DepositData_Post(ctx, depositData) + return m.v2Client.StakeWise.DepositData_Post(ctx, m.resources.DeploymentName, vault, depositData) }) if err != nil { return fmt.Errorf("error uploading deposit data: %w", err) @@ -200,7 +201,7 @@ func (m *NodeSetServiceManager) StakeWise_UploadDepositData(ctx context.Context, } // Get the version of the latest deposit data set from the server -func (m *NodeSetServiceManager) StakeWise_GetRegisteredValidators(ctx context.Context, vault common.Address) ([]apiv1.ValidatorStatus, error) { +func (m *NodeSetServiceManager) StakeWise_GetRegisteredValidators(ctx context.Context, vault common.Address) ([]stakewise.ValidatorStatus, error) { m.lock.Lock() defer m.lock.Unlock() @@ -212,10 +213,10 @@ func (m *NodeSetServiceManager) StakeWise_GetRegisteredValidators(ctx context.Co logger.Debug("Getting registered validators") // Run the request - var data apiv1.ValidatorsData + var data stakewise.ValidatorsData err := m.runRequest(ctx, func(ctx context.Context) error { var err error - data, err = m.v1Client.Validators_Get(ctx, m.resources.EthNetworkName) + data, err = m.v2Client.StakeWise.Validators_Get(ctx, m.resources.DeploymentName, vault) return err }) if err != nil { @@ -225,7 +226,7 @@ func (m *NodeSetServiceManager) StakeWise_GetRegisteredValidators(ctx context.Co } // Uploads signed exit messages set to the server -func (m *NodeSetServiceManager) StakeWise_UploadSignedExitMessages(ctx context.Context, exitData []apiv1.ExitData) error { +func (m *NodeSetServiceManager) StakeWise_UploadSignedExitMessages(ctx context.Context, vault common.Address, exitData []nscommon.ExitData) error { m.lock.Lock() defer m.lock.Unlock() @@ -238,7 +239,7 @@ func (m *NodeSetServiceManager) StakeWise_UploadSignedExitMessages(ctx context.C // Run the request err := m.runRequest(ctx, func(ctx context.Context) error { - return m.v1Client.Validators_Patch(ctx, exitData, m.resources.EthNetworkName) + return m.v2Client.StakeWise.Validators_Patch(ctx, m.resources.DeploymentName, vault, exitData) }) if err != nil { return fmt.Errorf("error uploading signed exit messages: %w", err) @@ -246,6 +247,154 @@ func (m *NodeSetServiceManager) StakeWise_UploadSignedExitMessages(ctx context.C return nil } +// ============================= +// === Constellation Methods === +// ============================= + +// Gets the address that has been registered by the node's user for Constellation. +// Returns nil if the user hasn't registered with NodeSet for Constellation usage yet. +func (m *NodeSetServiceManager) Constellation_GetRegisteredAddress(ctx context.Context) (*common.Address, error) { + m.lock.Lock() + defer m.lock.Unlock() + + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + logger.Debug("Getting registered Constellation address") + + // Run the request + var data v2constellation.Whitelist_GetData + err := m.runRequest(ctx, func(ctx context.Context) error { + var err error + data, err = m.v2Client.Constellation.Whitelist_Get(ctx, m.resources.HyperdriveResources.DeploymentName) + return err + }) + if err != nil { + return nil, fmt.Errorf("error getting registered Constellation address: %w", err) + } + logger.Debug("NodeSet responded", + slog.Bool("whitelisted", data.Whitelisted), + slog.String("address", data.Address.Hex()), + ) + + // Return the address if whitelisted + if data.Whitelisted { + return &data.Address, nil + } + return nil, nil +} + +// Gets a signature for registering / whitelisting the node with the Constellation contracts +func (m *NodeSetServiceManager) Constellation_GetRegistrationSignature(ctx context.Context) ([]byte, error) { + m.lock.Lock() + defer m.lock.Unlock() + + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + logger.Debug("Registering with the Constellation contracts") + + // Run the request + var data v2constellation.Whitelist_PostData + err := m.runRequest(ctx, func(ctx context.Context) error { + var err error + data, err = m.v2Client.Constellation.Whitelist_Post(ctx, m.resources.HyperdriveResources.DeploymentName) + return err + }) + if err != nil { + return nil, fmt.Errorf("error registering with Constellation: %w", err) + } + + // Decode the signature + sig, err := utils.DecodeHex(data.Signature) + if err != nil { + return nil, fmt.Errorf("error decoding signature from server: %w", err) + } + return sig, nil +} + +// Gets the deposit signature for a minipool from the Constellation contracts +func (m *NodeSetServiceManager) Constellation_GetDepositSignature(ctx context.Context, minipoolAddress common.Address, salt *big.Int) ([]byte, error) { + m.lock.Lock() + defer m.lock.Unlock() + + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + + // Run the request + var data v2constellation.MinipoolDepositSignatureData + logger.Debug("Getting minipool deposit signature") + err := m.runRequest(ctx, func(ctx context.Context) error { + var err error + data, err = m.v2Client.Constellation.MinipoolDepositSignature(ctx, m.resources.HyperdriveResources.DeploymentName, minipoolAddress, salt) + return err + }) + if err != nil { + return nil, fmt.Errorf("error getting deposit signature: %w", err) + } + + // Decode the signature + sig, err := utils.DecodeHex(data.Signature) + if err != nil { + return nil, fmt.Errorf("error decoding signature from server: %w", err) + } + return sig, nil +} + +// Get the validators that NodeSet has on record for this node +func (m *NodeSetServiceManager) Constellation_GetValidators(ctx context.Context) ([]v2constellation.ValidatorStatus, error) { + m.lock.Lock() + defer m.lock.Unlock() + + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + + // Run the request + var data v2constellation.ValidatorsData + logger.Debug("Getting validators for node") + err := m.runRequest(ctx, func(ctx context.Context) error { + var err error + data, err = m.v2Client.Constellation.Validators_Get(ctx, m.resources.HyperdriveResources.DeploymentName) + return err + }) + if err != nil { + return nil, fmt.Errorf("error getting validators for node: %w", err) + } + return data.Validators, nil +} + +// Upload signed exit messages for Constellation minipools to the NodeSet service +func (m *NodeSetServiceManager) Constellation_UploadSignedExitMessages(ctx context.Context, exitMessages []nscommon.ExitData) error { + m.lock.Lock() + defer m.lock.Unlock() + + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + + // Run the request + logger.Debug("Submitting signed exit messages to nodeset") + err := m.runRequest(ctx, func(ctx context.Context) error { + return m.v2Client.Constellation.Validators_Patch(ctx, m.resources.HyperdriveResources.DeploymentName, exitMessages) + }) + if err != nil { + return fmt.Errorf("error submitting signed exit messages: %w", err) + } + return nil +} + // ======================== // === Internal Methods === // ======================== @@ -255,7 +404,7 @@ func (m *NodeSetServiceManager) runRequest(ctx context.Context, request func(ctx // Run the request err := request(ctx) if err != nil { - if errors.Is(err, apiv1.ErrInvalidSession) { + if errors.Is(err, nscommon.ErrInvalidSession) { // Session expired so log in again err = m.loginImpl(ctx) if err != nil { @@ -284,7 +433,7 @@ func (m *NodeSetServiceManager) loginImpl(ctx context.Context) error { if err != nil { return fmt.Errorf("error getting wallet status for login: %w", err) } - err = services.CheckIfWalletReady(walletStatus) + err = CheckIfWalletReady(walletStatus) if err != nil { m.nodeRegistrationStatus = api.NodeSetRegistrationStatus_NoWallet return fmt.Errorf("can't log into nodeset, hyperdrive wallet not initialized yet") @@ -294,7 +443,7 @@ func (m *NodeSetServiceManager) loginImpl(ctx context.Context) error { logger.Info("Not authenticated with the NodeSet server, logging in") // Get the nonce - nonceData, err := m.v1Client.Nonce(ctx) + nonceData, err := m.v2Client.Core.Nonce(ctx) if err != nil { m.setRegistrationStatus(api.NodeSetRegistrationStatus_Unknown) return fmt.Errorf("error getting nonce for login: %w", err) @@ -306,22 +455,14 @@ func (m *NodeSetServiceManager) loginImpl(ctx context.Context) error { // Create a new session m.setSessionToken(nonceData.Token) - // Create the signature - message := fmt.Sprintf(apiv1.LoginMessageFormat, nonceData.Nonce, walletStatus.Wallet.WalletAddress.Hex()) - sigBytes, err := m.wallet.SignMessage([]byte(message)) - if err != nil { - m.setRegistrationStatus(api.NodeSetRegistrationStatus_Unknown) - return fmt.Errorf("error signing login message: %w", err) - } - // Attempt a login - loginData, err := m.v1Client.Login(ctx, nonceData.Nonce, walletStatus.Wallet.WalletAddress, sigBytes) + loginData, err := m.v2Client.Core.Login(ctx, nonceData.Nonce, walletStatus.Wallet.WalletAddress, m.wallet.SignMessage) if err != nil { if errors.Is(err, wallet.ErrWalletNotLoaded) { m.setRegistrationStatus(api.NodeSetRegistrationStatus_NoWallet) return err } - if errors.Is(err, apiv1.ErrUnregisteredNode) { + if errors.Is(err, core.ErrUnregisteredNode) { m.setRegistrationStatus(api.NodeSetRegistrationStatus_Unregistered) return nil } @@ -340,7 +481,7 @@ func (m *NodeSetServiceManager) loginImpl(ctx context.Context) error { // Sets the session token for the client after logging in func (m *NodeSetServiceManager) setSessionToken(sessionToken string) { m.sessionToken = sessionToken - m.v1Client.SetSessionToken(sessionToken) + m.v2Client.SetSessionToken(sessionToken) } // Sets the registration status of the node diff --git a/common/requirements.go b/common/requirements.go index d5a9e2a..5e19330 100644 --- a/common/requirements.go +++ b/common/requirements.go @@ -66,7 +66,7 @@ var ( // === Requirements === // ==================== -func (sp *ServiceProvider) RequireNodeAddress() error { +func (sp *serviceProvider) RequireNodeAddress() error { status, err := sp.GetWallet().GetStatus() if err != nil { return err @@ -77,7 +77,7 @@ func (sp *ServiceProvider) RequireNodeAddress() error { return nil } -func (sp *ServiceProvider) RequireWalletReady() error { +func (sp *serviceProvider) RequireWalletReady() error { status, err := sp.GetWallet().GetStatus() if err != nil { return err @@ -100,7 +100,7 @@ func (sp *ServiceProvider) RequireWalletReady() error { return nil } -func (sp *ServiceProvider) RequireEthClientSynced(ctx context.Context) error { +func (sp *serviceProvider) RequireEthClientSynced(ctx context.Context) error { synced, _, err := sp.checkExecutionClientStatus(ctx) if err != nil { return err @@ -111,7 +111,7 @@ func (sp *ServiceProvider) RequireEthClientSynced(ctx context.Context) error { return ErrExecutionClientNotSynced } -func (sp *ServiceProvider) RequireBeaconClientSynced(ctx context.Context) error { +func (sp *serviceProvider) RequireBeaconClientSynced(ctx context.Context) error { synced, err := sp.checkBeaconClientStatus(ctx) if err != nil { return err @@ -122,7 +122,7 @@ func (sp *ServiceProvider) RequireBeaconClientSynced(ctx context.Context) error return ErrBeaconNodeNotSynced } -func (sp *ServiceProvider) RequireRegisteredWithNodeSet(ctx context.Context) error { +func (sp *serviceProvider) RequireRegisteredWithNodeSet(ctx context.Context) error { status, err := sp.ns.GetRegistrationStatus(ctx) if err != nil { return err @@ -139,19 +139,19 @@ func (sp *ServiceProvider) RequireRegisteredWithNodeSet(ctx context.Context) err } // Wait for the Executon client to sync; timeout of 0 indicates no timeout -func (sp *ServiceProvider) WaitEthClientSynced(ctx context.Context, verbose bool) error { +func (sp *serviceProvider) WaitEthClientSynced(ctx context.Context, verbose bool) error { _, err := sp.waitEthClientSynced(ctx, verbose) return err } // Wait for the Beacon client to sync; timeout of 0 indicates no timeout -func (sp *ServiceProvider) WaitBeaconClientSynced(ctx context.Context, verbose bool) error { +func (sp *serviceProvider) WaitBeaconClientSynced(ctx context.Context, verbose bool) error { _, err := sp.waitBeaconClientSynced(ctx, verbose) return err } // Wait for the wallet to be ready -func (sp *ServiceProvider) WaitForWallet(ctx context.Context) error { +func (sp *serviceProvider) WaitForWallet(ctx context.Context) error { // Get the logger logger, exists := log.FromContext(ctx) if !exists { @@ -174,7 +174,7 @@ func (sp *ServiceProvider) WaitForWallet(ctx context.Context) error { // Wait until the node has been registered with NodeSet. // Returns true if the context was cancelled and the caller should exit. -func (sp *ServiceProvider) WaitForNodeSetRegistration(ctx context.Context) bool { +func (sp *serviceProvider) WaitForNodeSetRegistration(ctx context.Context) bool { // Get the logger logger, exists := log.FromContext(ctx) if !exists { @@ -213,7 +213,7 @@ func (sp *ServiceProvider) WaitForNodeSetRegistration(ctx context.Context) bool // Check if the primary and fallback Execution clients are synced // TODO: Move this into ec-manager and stop exposing the primary and fallback directly... -func (sp *ServiceProvider) checkExecutionClientStatus(ctx context.Context) (bool, eth.IExecutionClient, error) { +func (sp *serviceProvider) checkExecutionClientStatus(ctx context.Context) (bool, eth.IExecutionClient, error) { // Check the EC status ecMgr := sp.GetEthClient() mgrStatus := ecMgr.CheckStatus(ctx, true) // Always check the chain ID for now @@ -260,7 +260,7 @@ func (sp *ServiceProvider) checkExecutionClientStatus(ctx context.Context) (bool } // Check if the primary and fallback Beacon clients are synced -func (sp *ServiceProvider) checkBeaconClientStatus(ctx context.Context) (bool, error) { +func (sp *serviceProvider) checkBeaconClientStatus(ctx context.Context) (bool, error) { // Check the BC status bcMgr := sp.GetBeaconClient() mgrStatus := bcMgr.CheckStatus(ctx, true) // Always check the chain ID for now @@ -307,7 +307,7 @@ func (sp *ServiceProvider) checkBeaconClientStatus(ctx context.Context) (bool, e } // Wait for the primary or fallback Execution client to be synced -func (sp *ServiceProvider) waitEthClientSynced(ctx context.Context, verbose bool) (bool, error) { +func (sp *serviceProvider) waitEthClientSynced(ctx context.Context, verbose bool) (bool, error) { synced, clientToCheck, err := sp.checkExecutionClientStatus(ctx) if err != nil { return false, err @@ -380,7 +380,7 @@ func (sp *ServiceProvider) waitEthClientSynced(ctx context.Context, verbose bool } // Wait for the primary or fallback Beacon client to be synced -func (sp *ServiceProvider) waitBeaconClientSynced(ctx context.Context, verbose bool) (bool, error) { +func (sp *serviceProvider) waitBeaconClientSynced(ctx context.Context, verbose bool) (bool, error) { synced, err := sp.checkBeaconClientStatus(ctx) if err != nil { return false, err diff --git a/common/service-provider.go b/common/service-provider.go index 824f474..c64d43c 100644 --- a/common/service-provider.go +++ b/common/service-provider.go @@ -1,6 +1,7 @@ package common import ( + "context" "fmt" "os" "path/filepath" @@ -10,24 +11,91 @@ import ( "github.com/rocket-pool/node-manager-core/node/services" ) +// ================== +// === Interfaces === +// ================== + +// Provides Hyperdrive's configuration +type IHyperdriveConfigProvider interface { + // Gets Hyperdrive's configuration + GetConfig() *hdconfig.HyperdriveConfig + + // Gets Hyperdrive's list of resources + GetResources() *hdconfig.MergedResources +} + +// Provides a manager for nodeset.io communications +type INodeSetManagerProvider interface { + // Gets the NodeSetServiceManager + GetNodeSetServiceManager() *NodeSetServiceManager +} + +// Provides methods for requiring or waiting for various conditions to be met +type IRequirementsProvider interface { + // Require Hyperdrive has a node address set + RequireNodeAddress() error + + // Require Hyperdrive has a wallet that's loaded and ready for transactions + RequireWalletReady() error + + // Require that the Ethereum client is synced + RequireEthClientSynced(ctx context.Context) error + + // Require that the Beacon chain client is synced + RequireBeaconClientSynced(ctx context.Context) error + + // Require the node has been registered with a nodeset.io account + RequireRegisteredWithNodeSet(ctx context.Context) error + + // Wait for the Ethereum client to be synced + WaitEthClientSynced(ctx context.Context, verbose bool) error + + // Wait for the Beacon chain client to be synced + WaitBeaconClientSynced(ctx context.Context, verbose bool) error + + // Wait for the node to have a wallet loaded and ready for transactions + WaitForWallet(ctx context.Context) error + + // Wait for the node to be registered with a nodeset.io account + WaitForNodeSetRegistration(ctx context.Context) bool +} + +// Provides access to all of Hyperdrive's services +type IHyperdriveServiceProvider interface { + IHyperdriveConfigProvider + INodeSetManagerProvider + IRequirementsProvider + services.IServiceProvider +} + +// ======================== +// === Service Provider === +// ======================== + // A container for all of the various services used by Hyperdrive -type ServiceProvider struct { - *services.ServiceProvider +type serviceProvider struct { + services.IServiceProvider // Services cfg *hdconfig.HyperdriveConfig - res *hdconfig.HyperdriveResources + res *hdconfig.MergedResources ns *NodeSetServiceManager // Path info userDir string } -// Creates a new ServiceProvider instance by loading the Hyperdrive config in the provided directory -func NewServiceProvider(userDir string) (*ServiceProvider, error) { - // Config +// Creates a new IHyperdriveServiceProvider instance by loading the Hyperdrive config in the provided directory +func NewHyperdriveServiceProvider(userDir string, resourcesDir string) (IHyperdriveServiceProvider, error) { + // Load the network settings + settingsList, err := hdconfig.LoadSettingsFiles(resourcesDir) + if err != nil { + return nil, fmt.Errorf("error loading network settings: %w", err) + } + + // Create the config cfgPath := filepath.Join(userDir, hdconfig.ConfigFilename) - cfg, err := loadConfigFromFile(os.ExpandEnv(cfgPath)) + cfg, err := loadConfigFromFile(os.ExpandEnv(cfgPath), settingsList) if err != nil { return nil, fmt.Errorf("error loading hyperdrive config: %w", err) } @@ -35,31 +103,46 @@ func NewServiceProvider(userDir string) (*ServiceProvider, error) { return nil, fmt.Errorf("hyperdrive config settings file [%s] not found", cfgPath) } - return NewServiceProviderFromConfig(cfg) + // Get the resources from the selected network + var selectedResources *hdconfig.MergedResources + for _, network := range settingsList { + if network.Key == cfg.Network.Value { + selectedResources = &hdconfig.MergedResources{ + NetworkResources: network.NetworkResources, + HyperdriveResources: network.HyperdriveResources, + } + break + } + } + if selectedResources == nil { + return nil, fmt.Errorf("no resources found for selected network [%s]", cfg.Network.Value) + } + + return NewHyperdriveServiceProviderFromConfig(cfg, selectedResources) } -// Creates a new ServiceProvider instance directly from a Hyperdrive config instead of loading it from the filesystem -func NewServiceProviderFromConfig(cfg *hdconfig.HyperdriveConfig) (*ServiceProvider, error) { +// Creates a new IHyperdriveServiceProvider instance directly from a Hyperdrive config and resources list instead of loading them from the filesystem +func NewHyperdriveServiceProviderFromConfig(cfg *hdconfig.HyperdriveConfig, resources *hdconfig.MergedResources) (IHyperdriveServiceProvider, error) { // Core provider - sp, err := services.NewServiceProvider(cfg, hdconfig.ClientTimeout) + sp, err := services.NewServiceProvider(cfg, resources.NetworkResources, hdconfig.ClientTimeout) if err != nil { return nil, fmt.Errorf("error creating core service provider: %w", err) } // Create the provider - provider := &ServiceProvider{ - ServiceProvider: sp, - userDir: cfg.GetUserDirectory(), - cfg: cfg, - res: cfg.GetResources(), + provider := &serviceProvider{ + IServiceProvider: sp, + userDir: cfg.GetUserDirectory(), + cfg: cfg, + res: resources, } ns := NewNodeSetServiceManager(provider) provider.ns = ns return provider, nil } -// Creates a new ServiceProvider instance from custom services and artifacts -func NewServiceProviderFromCustomServices(cfg *hdconfig.HyperdriveConfig, resources *hdconfig.HyperdriveResources, ecManager *services.ExecutionClientManager, bnManager *services.BeaconClientManager, docker client.APIClient) (*ServiceProvider, error) { +// Creates a new IHyperdriveServiceProvider instance from custom services and artifacts +func NewHyperdriveServiceProviderFromCustomServices(cfg *hdconfig.HyperdriveConfig, resources *hdconfig.MergedResources, ecManager *services.ExecutionClientManager, bnManager *services.BeaconClientManager, docker client.APIClient) (IHyperdriveServiceProvider, error) { // Core provider sp, err := services.NewServiceProviderWithCustomServices(cfg, resources.NetworkResources, ecManager, bnManager, docker) if err != nil { @@ -67,11 +150,11 @@ func NewServiceProviderFromCustomServices(cfg *hdconfig.HyperdriveConfig, resour } // Create the provider - provider := &ServiceProvider{ - ServiceProvider: sp, - userDir: cfg.GetUserDirectory(), - cfg: cfg, - res: resources, + provider := &serviceProvider{ + IServiceProvider: sp, + userDir: cfg.GetUserDirectory(), + cfg: cfg, + res: resources, } ns := NewNodeSetServiceManager(provider) provider.ns = ns @@ -82,19 +165,15 @@ func NewServiceProviderFromCustomServices(cfg *hdconfig.HyperdriveConfig, resour // === Getters === // =============== -func (p *ServiceProvider) GetUserDir() string { - return p.userDir -} - -func (p *ServiceProvider) GetConfig() *hdconfig.HyperdriveConfig { +func (p *serviceProvider) GetConfig() *hdconfig.HyperdriveConfig { return p.cfg } -func (p *ServiceProvider) GetResources() *hdconfig.HyperdriveResources { +func (p *serviceProvider) GetResources() *hdconfig.MergedResources { return p.res } -func (p *ServiceProvider) GetNodeSetServiceManager() *NodeSetServiceManager { +func (p *serviceProvider) GetNodeSetServiceManager() *NodeSetServiceManager { return p.ns } @@ -103,13 +182,13 @@ func (p *ServiceProvider) GetNodeSetServiceManager() *NodeSetServiceManager { // ============= // Loads a Hyperdrive config without updating it if it exists -func loadConfigFromFile(path string) (*hdconfig.HyperdriveConfig, error) { - _, err := os.Stat(path) +func loadConfigFromFile(configPath string, networks []*hdconfig.HyperdriveSettings) (*hdconfig.HyperdriveConfig, error) { + _, err := os.Stat(configPath) if os.IsNotExist(err) { return nil, nil } - cfg, err := hdconfig.LoadFromFile(path) + cfg, err := hdconfig.LoadFromFile(configPath, networks) if err != nil { return nil, err } diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 0000000..80e5ebd --- /dev/null +++ b/common/utils.go @@ -0,0 +1,26 @@ +package common + +import ( + "errors" + + "github.com/rocket-pool/node-manager-core/wallet" +) + +func CheckIfWalletReady(status wallet.WalletStatus) error { + if !status.Address.HasAddress { + return errors.New("The node currently does not have an address set. Please run 'hyperdrive wallet init' and try again.") + } + if !status.Wallet.IsLoaded { + if status.Wallet.IsOnDisk { + if !status.Password.IsPasswordSaved { + return errors.New("The node has a node wallet on disk but does not have the password for it loaded. Please run `hyperdrive wallet set-password` to load it.") + } + return errors.New("The node has a node wallet and a password on disk but there was an error loading it - perhaps the password is incorrect? Please check the node logs for more information.") + } + return errors.New("The node currently does not have a node wallet keystore. Please run 'hyperdrive wallet init' and try again.") + } + if status.Wallet.WalletAddress != status.Address.NodeAddress { + return errors.New("The node's wallet keystore does not match the node address. This node is currently in read-only mode.") + } + return nil +} diff --git a/docker/daemon-build.dockerfile b/docker/daemon-build.dockerfile index 4f92941..901237e 100644 --- a/docker/daemon-build.dockerfile +++ b/docker/daemon-build.dockerfile @@ -1,5 +1,5 @@ # The builder for building the daemon -FROM --platform=${BUILDPLATFORM} golang:1.21-bookworm AS builder +FROM --platform=${BUILDPLATFORM} golang:1.22-bookworm AS builder ARG TARGETOS TARGETARCH BUILDPLATFORM COPY . /hyperdrive-daemon ENV CGO_ENABLED=1 diff --git a/go.mod b/go.mod index d312971..9103a39 100644 --- a/go.mod +++ b/go.mod @@ -1,101 +1,100 @@ module github.com/nodeset-org/hyperdrive-daemon -go 1.21 +go 1.22 -toolchain go1.21.10 +toolchain go1.22.7 require ( github.com/alessio/shellescape v1.4.2 - github.com/docker/docker v26.1.0+incompatible - github.com/ethereum/go-ethereum v1.14.3 - github.com/fatih/color v1.16.0 - github.com/goccy/go-json v0.10.2 + github.com/docker/docker v27.0.3+incompatible + github.com/ethereum/go-ethereum v1.14.8 + github.com/fatih/color v1.17.0 + github.com/goccy/go-json v0.10.3 github.com/gorilla/mux v1.8.1 github.com/hashicorp/go-version v1.6.0 - github.com/nodeset-org/nodeset-client-go v0.0.0-20240705161624-e301897d5d3c - github.com/nodeset-org/osha v0.2.0 + github.com/nodeset-org/nodeset-client-go v1.0.0 + github.com/nodeset-org/osha v0.3.0 github.com/rocket-pool/batch-query v1.0.0 - github.com/rocket-pool/node-manager-core v0.5.1 + github.com/rocket-pool/node-manager-core v0.5.2-0.20240918224929-a9cee1201bee github.com/stretchr/testify v1.9.0 - github.com/urfave/cli/v2 v2.27.1 + github.com/urfave/cli/v2 v2.27.2 github.com/wealdtech/go-ens/v3 v3.6.0 + golang.org/x/sync v0.7.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect - github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20240716105424-66b64c4bb379 // indirect + github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.12.3 // indirect + github.com/Microsoft/hcsshim v0.12.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect - github.com/btcsuite/btcd v0.24.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd v0.24.2 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cilium/ebpf v0.11.0 // indirect - github.com/compose-spec/compose-go/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/compose-spec/compose-go/v2 v2.1.3 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect - github.com/containerd/containerd v1.7.17 // indirect + github.com/containerd/containerd v1.7.19 // indirect + github.com/containerd/containerd/api v1.7.19 // indirect github.com/containerd/continuity v0.4.3 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/ttrpc v1.2.4 // indirect - github.com/containerd/typeurl/v2 v2.1.1 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/containerd/ttrpc v1.2.5 // indirect + github.com/containerd/typeurl/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ethereum/c-kzg-4844 v1.0.1 // indirect + github.com/ethereum/c-kzg-4844 v1.0.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/ferranbt/fastssz v0.1.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/glendc/go-external-ip v0.1.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.4 // indirect - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/herumi/bls-eth-go-binary v1.33.0 // indirect - github.com/holiman/uint256 v1.2.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/herumi/bls-eth-go-binary v1.36.1 // indirect + github.com/holiman/uint256 v1.3.1 // indirect github.com/ipfs/go-cid v0.4.1 // indirect - github.com/klauspost/compress v1.17.6 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/swarmkit/v2 v2.0.0-20240415162501-c1c857e2dca1 // indirect + github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb // indirect github.com/moby/sys/mount v0.3.3 // indirect github.com/moby/sys/mountinfo v0.7.1 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -111,6 +110,7 @@ require ( github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect @@ -118,23 +118,23 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.51.1 // indirect - github.com/prometheus/procfs v0.13.0 // indirect - github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 // indirect - github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 // indirect - github.com/prysmaticlabs/gohashtree v0.0.4-beta // indirect - github.com/prysmaticlabs/prysm/v5 v5.0.3 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3 // indirect + github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e // indirect + github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b // indirect + github.com/prysmaticlabs/prysm/v5 v5.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sethvargo/go-password v0.2.0 // indirect + github.com/sethvargo/go-password v0.3.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.12 // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect - github.com/tklauser/go-sysconf v0.3.13 // indirect - github.com/tklauser/numcpus v0.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/wealdtech/go-bytesutil v1.2.1 // indirect @@ -145,26 +145,25 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.etcd.io/etcd/raft/v3 v3.5.6 // indirect + go.etcd.io/etcd/raft/v3 v3.5.14 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.27.0 // indirect - go.opentelemetry.io/otel/trace v1.27.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20240716160929-1d5bc16f04a8 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect - google.golang.org/grpc v1.63.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 66a21bb..e74f556 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI= contrib.go.opencensus.io/exporter/jaeger v0.2.1/go.mod h1:Y8IsLgdxqh1QxYxPC5IgXVmBaeLUeQFfBeBi9PbeZd0= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240716105424-66b64c4bb379 h1:shYAfOpsleWVaSwGxQjmi+BBIwzj5jxB1FTCpVqs0N8= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240716105424-66b64c4bb379/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -14,8 +14,8 @@ github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.12.3 h1:LS9NXqXhMoqNCplK1ApmVSfB4UnVLRDWRapB6EIlxE0= -github.com/Microsoft/hcsshim v0.12.3/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= +github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0= +github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -34,12 +34,12 @@ github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6 github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= -github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -60,39 +60,36 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= -github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= -github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= -github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= +github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/compose-spec/compose-go/v2 v2.1.1 h1:tKuYJwAVgxIryRrsvWJSf1kNviVOQVVqwyHsV6YoIUc= -github.com/compose-spec/compose-go/v2 v2.1.1/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc= +github.com/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi1KW7R5esrLE= +github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= -github.com/containerd/containerd v1.7.17 h1:KjNnn0+tAVQHAoaWRjmdak9WlvnFR/8rU1CHHy8Rm2A= -github.com/containerd/containerd v1.7.17/go.mod h1:vK+hhT4TIv2uejlcDlbVIc8+h/BqtKLIyNrtCZol8lI= +github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE= +github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc= +github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= +github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= @@ -101,17 +98,16 @@ github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/ttrpc v1.2.4 h1:eQCQK4h9dxDmpOb9QOOMh2NHTfzroH1IkmHiKZi05Oo= -github.com/containerd/ttrpc v1.2.4/go.mod h1:ojvb8SJBSch0XkqNO0L0YX/5NxR3UnVk2LzFKBK0upc= -github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= -github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU= +github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso= +github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -128,15 +124,15 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v26.1.0+incompatible h1:W1G9MPNbskA6VZWL7b3ZljTh0pXI68FpINx0GKaOdaM= -github.com/docker/docker v26.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE= +github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= @@ -149,54 +145,47 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= -github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.3 h1:5zvnAqLtnCZrU9uod1JCvHWJbPMURzYFHfc2eHz4PHA= -github.com/ethereum/go-ethereum v1.14.3/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/ethereum/c-kzg-4844 v1.0.2 h1:8tV84BCEiPeOkiVgW9mpYBeBUir2bkCNVqxPwwVeO+s= +github.com/ethereum/c-kzg-4844 v1.0.2/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig= +github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= -github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/glendc/go-external-ip v0.1.0 h1:iX3xQ2Q26atAmLTbd++nUce2P5ht5P4uD4V7caSY/xg= github.com/glendc/go-external-ip v0.1.0/go.mod h1:CNx312s2FLAJoWNdJWZ2Fpf5O4oLsMFwuYviHjS4uJE= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= @@ -217,8 +206,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -230,7 +217,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -243,10 +229,10 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -260,16 +246,16 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/herumi/bls-eth-go-binary v1.33.0 h1:fHoysK+WbL/FQIJoVGECGd2lBLa2De7YjAGZljI2vzQ= -github.com/herumi/bls-eth-go-binary v1.33.0/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/herumi/bls-eth-go-binary v1.36.1 h1:SfLjxbO1fWkKtKS7J3Ezd1/5QXrcaTZgWynxdSe10hQ= +github.com/herumi/bls-eth-go-binary v1.36.1/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= -github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -285,21 +271,17 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= -github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -316,18 +298,14 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -337,8 +315,8 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/swarmkit/v2 v2.0.0-20240415162501-c1c857e2dca1 h1:F3L5Rx1jDTbUn/2U9Kb6tsNAl4MhQ+cdTiidA/qAiEA= -github.com/moby/swarmkit/v2 v2.0.0-20240415162501-c1c857e2dca1/go.mod h1:kNy225f/gWAnF8wPftteMc5nbAHhrH+HUfvyjmhFjeQ= +github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb h1:1UTTg2EgO3nuyV03wREDzldqqePzQ4+0a5G1C1y1bIo= +github.com/moby/swarmkit/v2 v2.0.0-20240611172349-ea1a7cec35cb/go.mod h1:kNy225f/gWAnF8wPftteMc5nbAHhrH+HUfvyjmhFjeQ= github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs= github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= @@ -377,11 +355,13 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nodeset-org/nodeset-client-go v0.0.0-20240705161624-e301897d5d3c h1:eu2hiqhN4+RFU9kcxxAFhCiqAHU9yXU+mVUZkF9Hi40= -github.com/nodeset-org/nodeset-client-go v0.0.0-20240705161624-e301897d5d3c/go.mod h1:a38gBF1yDdFduCb7IJF9xUZ4IFYsYKX9JGYAd7IaLNQ= -github.com/nodeset-org/osha v0.2.0 h1:k/rqy8Qun/EAnodU+yZy98OSHWK36PzmBfLXvb2uVw8= -github.com/nodeset-org/osha v0.2.0/go.mod h1:qkXYqNHUom/vRxlP7sj7Ilq1bvaSv4AhjfyOAGIwoSk= +github.com/nodeset-org/nodeset-client-go v1.0.0 h1:ZbYdP3nEFuZMT3Yt0nbBIYMdQZ4rAHMccvKjnUPZxhc= +github.com/nodeset-org/nodeset-client-go v1.0.0/go.mod h1:slpwejkJ/vYU9SKA3SYw4hIPJLzDUeQxcx+Cm04kkIA= +github.com/nodeset-org/osha v0.3.0 h1:oX9ZrFLKXhhxCWbABY4VAvsqvGUH790XNeENl46a4EY= +github.com/nodeset-org/osha v0.3.0/go.mod h1:jGCgmYNYxI97YgITz2Xwpy8689vVnCF8fDsmB4Q32F8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -406,7 +386,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -414,46 +393,46 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= -github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= -github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= -github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44 h1:c3p3UzV4vFA7xaCDphnDWOjpxcadrQ26l5b+ypsvyxo= -github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44/go.mod h1:MA5zShstUwCQaE9faGHgCGvEWUbG87p4SAXINhmCkvg= -github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= -github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= -github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= -github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3 h1:0LZAwwHnsZFfXm4IK4rzFV4N5IVSKZKLmuBMA4kAlFk= +github.com/prysmaticlabs/fastssz v0.0.0-20240620202422-a981b8ef89d3/go.mod h1:h2OlIZD/M6wFvV3YMZbW16lFgh3Rsye00G44J2cwLyU= +github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe+abbzfx9kCPeXIW4fiWyDdxlwHw07j8UGhdTd4= +github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1:VK7thFOnhxAZ/5aolr5Os4beiubuD08WiuiHyRqgwks= +github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY= github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU= github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y= -github.com/prysmaticlabs/prysm/v5 v5.0.3 h1:hUi0gu6v7aXmMQkl2GbrLoWcMhDNIbkVxRwrZchKbxU= -github.com/prysmaticlabs/prysm/v5 v5.0.3/go.mod h1:v5Oz4A4cWljfxUmW7SDk/VBzoYnei+lzwJogvSqUZVs= +github.com/prysmaticlabs/prysm/v5 v5.1.0 h1:TY9A6tm0v7bI1z9YH+xkDh7XH7qm4ZK8sTeyckxbj4A= +github.com/prysmaticlabs/prysm/v5 v5.1.0/go.mod h1:SWb5kE/FhtQrLS2yt+IDj+leB7IhXrcOv6lhDnU1nBY= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rocket-pool/batch-query v1.0.0 h1:5HejmT1n1fIdLIqUhTNwbkG2PGOPl3IVjCpFQcQZ4I4= github.com/rocket-pool/batch-query v1.0.0/go.mod h1:d1CmxShzk0fioJ4yX0eFGhz2an1odnW/LZ2cp3eDGIQ= -github.com/rocket-pool/node-manager-core v0.5.1 h1:mpPSGsSYaYdy3xdn7RIUcunGtbOprlFeyB9jknCUr5c= -github.com/rocket-pool/node-manager-core v0.5.1/go.mod h1:Clii5aca9PvR4HoAlUs8dh2OsJbDDnJ4yL5EaQE1gSo= +github.com/rocket-pool/node-manager-core v0.5.2-0.20240918224929-a9cee1201bee h1:iKnfrO0dz45YsB03qVFo2AN6ydG682aUwrGUpXAmWAY= +github.com/rocket-pool/node-manager-core v0.5.2-0.20240918224929-a9cee1201bee/go.mod h1:tLL93eZeU8hkwuMkMl/2ERv82cGHLNFufq8kedsrtSo= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= -github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= +github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= +github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -475,24 +454,24 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.12 h1:Vfas2U2CFHhniv2QkUm2OVa1+pGTdqtpqm9NnhUUbZ8= +github.com/supranational/blst v0.3.12/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo= github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= -github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= -github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= -github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= -github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/wealdtech/go-bytesutil v1.2.1 h1:TjuRzcG5KaPwaR5JB7L/OgJqMQWvlrblA1n0GfcXFSY= @@ -518,48 +497,42 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= -github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/raft/v3 v3.5.6 h1:tOmx6Ym6rn2GpZOrvTGJZciJHek6RnC3U/zNInzIN50= -go.etcd.io/etcd/raft/v3 v3.5.6/go.mod h1:wL8kkRGx1Hp8FmZUuHfL3K2/OaGIDaXGr1N7i2G07J0= +go.etcd.io/etcd/raft/v3 v3.5.14 h1:mHnpbljpBBftmK+YUfp+49ivaCc126aBPLAnwDw0DnE= +go.etcd.io/etcd/raft/v3 v3.5.14/go.mod h1:WnIK5blyJGRKsHA3efovdNoLv9QELTZHzpDOVIAuL2s= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240716160929-1d5bc16f04a8 h1:Z+vTUQyBb738QmIhbJx3z4htsxDeI+rd0EHvNm8jHkg= +golang.org/x/exp v0.0.0-20240716160929-1d5bc16f04a8/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -580,11 +553,11 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -597,7 +570,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -609,7 +581,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -618,19 +589,19 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -638,8 +609,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -648,24 +619,22 @@ google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1:Jt1XW5PaLXF1/ePZrznsh/aAUvI7Adfc3LY1dAKlzRs= -google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= +google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= +google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -675,11 +644,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -692,12 +658,10 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/hyperdrive-daemon.go b/hyperdrive-daemon.go index 09e4450..e1a4feb 100644 --- a/hyperdrive-daemon.go +++ b/hyperdrive-daemon.go @@ -47,6 +47,12 @@ func main() { Usage: "The path of the user data directory, which contains the configuration file to load and all of the user's runtime data", Required: true, } + settingsFolderFlag := &cli.StringFlag{ + Name: "settings-folder", + Aliases: []string{"s"}, + Usage: "The path to the folder containing the network settings files", + Required: true, + } ipFlag := &cli.StringFlag{ Name: "ip", Aliases: []string{"i"}, @@ -62,11 +68,12 @@ func main() { app.Flags = []cli.Flag{ userDirFlag, + settingsFolderFlag, ipFlag, portFlag, } app.Action = func(c *cli.Context) error { - // Get the config file + // Get the config file path userDir := c.String(userDirFlag.Name) cfgPath := filepath.Join(userDir, config.ConfigFilename) _, err := os.Stat(cfgPath) @@ -75,11 +82,23 @@ func main() { os.Exit(1) } + // Get the settings file path + settingsFolder := c.String(settingsFolderFlag.Name) + if settingsFolder == "" { + fmt.Println("No settings folder provided.") + os.Exit(1) + } + _, err = os.Stat(settingsFolder) + if errors.Is(err, fs.ErrNotExist) { + fmt.Printf("Settings folder not found at [%s].", settingsFolder) + os.Exit(1) + } + // Wait group to handle graceful stopping stopWg := new(sync.WaitGroup) // Create the service provider - sp, err := common.NewServiceProvider(userDir) + sp, err := common.NewHyperdriveServiceProvider(userDir, settingsFolder) if err != nil { return fmt.Errorf("error creating service provider: %w", err) } diff --git a/internal/tests/api/main_test.go b/internal/tests/api/main_test.go index d1c3519..6c3e58e 100644 --- a/internal/tests/api/main_test.go +++ b/internal/tests/api/main_test.go @@ -8,6 +8,7 @@ import ( "testing" hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/rocket-pool/node-manager-core/config" "github.com/rocket-pool/node-manager-core/log" ) @@ -16,17 +17,21 @@ var ( testMgr *hdtesting.HyperdriveTestManager = nil wg *sync.WaitGroup = nil logger *slog.Logger = nil + hdNode *hdtesting.HyperdriveNode ) // Initialize a common server used by all tests func TestMain(m *testing.M) { wg = &sync.WaitGroup{} var err error - testMgr, err = hdtesting.NewHyperdriveTestManagerWithDefaults("localhost", "localhost") + testMgr, err = hdtesting.NewHyperdriveTestManagerWithDefaults(func(ns *config.NetworkSettings) *config.NetworkSettings { + return ns + }) if err != nil { fail("error creating test manager: %v", err) } logger = testMgr.GetLogger() + hdNode = testMgr.GetNode() // Run tests code := m.Run() diff --git a/internal/tests/api/nodeset_test.go b/internal/tests/api/nodeset_test.go new file mode 100644 index 0000000..5b9f971 --- /dev/null +++ b/internal/tests/api/nodeset_test.go @@ -0,0 +1,124 @@ +package api_test + +import ( + "runtime/debug" + "testing" + + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/nodeset-org/osha/keys" + "github.com/rocket-pool/node-manager-core/wallet" + "github.com/stretchr/testify/require" +) + +const ( + nsEmail string = "test@nodeset.io" +) + +// Test registration with nodeset.io if the node doesn't have a wallet yet +func TestNodeSetRegistration_NoWallet(t *testing.T) { + // Take a snapshot, revert at the end + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem | hdtesting.Service_NodeSet) + if err != nil { + fail("Error creating custom snapshot: %v", err) + } + defer nodeset_cleanup(snapshotName) + + // Run the round-trip test + hd := hdNode.GetApiClient() + response, err := hd.NodeSet.GetRegistrationStatus() + require.NoError(t, err) + require.Equal(t, api.NodeSetRegistrationStatus_NoWallet, response.Data.Status) + t.Logf("Node has no wallet, registration status is correct") +} + +// Test registration with nodeset.io if the node has a wallet but hasn't been registered yet +func TestNodeSetRegistration_NoRegistration(t *testing.T) { + // Take a snapshot, revert at the end + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem | hdtesting.Service_NodeSet) + if err != nil { + fail("Error creating custom snapshot: %v", err) + } + defer nodeset_cleanup(snapshotName) + + // Recover a wallet + derivationPath := string(wallet.DerivationPath_Default) + index := uint64(0) + recoverResponse, err := hdNode.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) + require.NoError(t, err) + t.Log("Recover called") + + // Check the response + require.Equal(t, expectedWalletAddress, recoverResponse.Data.AccountAddress) + t.Log("Received correct wallet address") + + // Run the round-trip test + hd := hdNode.GetApiClient() + registrationResponse, err := hd.NodeSet.GetRegistrationStatus() + require.NoError(t, err) + require.Equal(t, api.NodeSetRegistrationStatus_Unregistered, registrationResponse.Data.Status) + t.Logf("Node has a wallet but isn't registered, registration status is correct") +} + +// Test registration with nodeset.io if the node has a wallet and has been registered +func TestNodeSetRegistration_Registered(t *testing.T) { + // Take a snapshot, revert at the end + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem | hdtesting.Service_NodeSet) + if err != nil { + fail("Error creating custom snapshot: %v", err) + } + defer nodeset_cleanup(snapshotName) + + // Recover a wallet + derivationPath := string(wallet.DerivationPath_Default) + index := uint64(0) + recoverResponse, err := hdNode.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) + require.NoError(t, err) + t.Log("Recover called") + + // Check the response + require.Equal(t, expectedWalletAddress, recoverResponse.Data.AccountAddress) + t.Log("Received correct wallet address") + + // Register the node with nodeset.io + hd := hdNode.GetApiClient() + nsMgr := testMgr.GetNodeSetMockServer().GetManager() + nsDB := nsMgr.GetDatabase() + user, err := nsDB.Core.AddUser(nsEmail) + require.NoError(t, err) + _ = user.WhitelistNode(expectedWalletAddress) + require.NoError(t, err) + registerResponse, err := hd.NodeSet.RegisterNode(nsEmail) + require.NoError(t, err) + require.True(t, registerResponse.Data.Success) + + // Run the round-trip test + registrationResponse, err := hd.NodeSet.GetRegistrationStatus() + require.NoError(t, err) + require.Equal(t, api.NodeSetRegistrationStatus_Registered, registrationResponse.Data.Status) + t.Logf("Node is registered with nodeset.io") +} + +// Cleanup after a unit test +func nodeset_cleanup(snapshotName string) { + // Handle panics + r := recover() + if r != nil { + debug.PrintStack() + fail("Recovered from panic: %v", r) + } + + // Revert to the snapshot taken at the start of the test + if snapshotName != "" { + err := testMgr.RevertToCustomSnapshot(snapshotName) + if err != nil { + fail("Error reverting to custom snapshot: %v", err) + } + } + + // Reload the wallet to undo any changes made during the test + err := hdNode.GetServiceProvider().GetWallet().Reload(testMgr.GetLogger()) + if err != nil { + fail("Error reloading wallet: %v", err) + } +} diff --git a/internal/tests/api/service_test.go b/internal/tests/api/service_test.go index 14b9f56..ac1f536 100644 --- a/internal/tests/api/service_test.go +++ b/internal/tests/api/service_test.go @@ -7,14 +7,14 @@ import ( dtypes "github.com/docker/docker/api/types" "github.com/nodeset-org/hyperdrive-daemon/shared" - "github.com/nodeset-org/osha" + hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" "github.com/stretchr/testify/require" ) // Test getting the client status of synced clients func TestClientStatus_Synced(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -28,7 +28,7 @@ func TestClientStatus_Synced(t *testing.T) { } // Run the round-trip test - response, err := testMgr.GetApiClient().Service.ClientStatus() + response, err := hdNode.GetApiClient().Service.ClientStatus() require.NoError(t, err) require.True(t, response.Data.EcManagerStatus.PrimaryClientStatus.IsSynced) require.True(t, response.Data.EcManagerStatus.PrimaryClientStatus.IsWorking) @@ -53,7 +53,7 @@ func TestServerVersion(t *testing.T) { version := shared.HyperdriveVersion // Run the round-trip test - response, err := testMgr.GetApiClient().Service.Version() + response, err := hdNode.GetApiClient().Service.Version() require.NoError(t, err) require.Equal(t, version, response.Data.Version) t.Logf("Received correct version: %s", version) @@ -61,14 +61,14 @@ func TestServerVersion(t *testing.T) { func TestRestartContainer(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_Docker) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Docker) if err != nil { fail("Error creating custom snapshot: %v", err) } defer service_cleanup(snapshotName) // Get some services - sp := testMgr.GetServiceProvider() + sp := hdNode.GetServiceProvider() cfg := sp.GetConfig() ctx := sp.GetBaseContext() @@ -96,7 +96,7 @@ func TestRestartContainer(t *testing.T) { t.Log("Created mock VC") // Run the client call - _, err = testMgr.GetApiClient().Service.RestartContainer(containerName) + _, err = hdNode.GetApiClient().Service.RestartContainer(containerName) require.NoError(t, err) t.Log("Restart called") diff --git a/internal/tests/api/wallet_test.go b/internal/tests/api/wallet_test.go index 30d5d8b..37d1008 100644 --- a/internal/tests/api/wallet_test.go +++ b/internal/tests/api/wallet_test.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/nodeset-org/osha" + hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" "github.com/nodeset-org/osha/keys" "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/node-manager-core/wallet" @@ -29,7 +29,7 @@ var ( func TestWalletRecover_Success(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -38,7 +38,7 @@ func TestWalletRecover_Success(t *testing.T) { // Run the round-trip test derivationPath := string(wallet.DerivationPath_Default) index := uint64(0) - response, err := testMgr.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) + response, err := hdNode.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) require.NoError(t, err) t.Log("Recover called") @@ -49,7 +49,7 @@ func TestWalletRecover_Success(t *testing.T) { func TestWalletRecover_WrongIndex(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -58,7 +58,7 @@ func TestWalletRecover_WrongIndex(t *testing.T) { // Run the round-trip test derivationPath := string(wallet.DerivationPath_Default) index := uint64(1) - response, err := testMgr.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) + response, err := hdNode.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) require.NoError(t, err) t.Log("Recover called") @@ -68,7 +68,7 @@ func TestWalletRecover_WrongIndex(t *testing.T) { } func TestWalletRecover_WrongDerivationPath(t *testing.T) { - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -77,7 +77,7 @@ func TestWalletRecover_WrongDerivationPath(t *testing.T) { // Run the round-trip test derivationPath := string(wallet.DerivationPath_LedgerLive) index := uint64(0) - response, err := testMgr.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) + response, err := hdNode.GetApiClient().Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) require.NoError(t, err) t.Log("Recover called") @@ -87,7 +87,7 @@ func TestWalletRecover_WrongDerivationPath(t *testing.T) { } func TestWalletStatus_NotLoaded(t *testing.T) { - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() response, err := apiClient.Wallet.Status() require.NoError(t, err) t.Log("Status called") @@ -105,7 +105,7 @@ func TestWalletStatus_NotLoaded(t *testing.T) { func TestWalletStatus_Loaded(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients | osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -121,7 +121,7 @@ func TestWalletStatus_Loaded(t *testing.T) { // Regen the wallet derivationPath := string(wallet.DerivationPath_Default) index := uint64(0) - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() _, err = apiClient.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) require.NoError(t, err) t.Log("Recover called") @@ -143,7 +143,7 @@ func TestWalletStatus_Loaded(t *testing.T) { func TestWalletBalance(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients | osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -157,9 +157,9 @@ func TestWalletBalance(t *testing.T) { } // Regen the wallet - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() derivationPath := string(wallet.DerivationPath_Default) - index := uint64(0) + index := uint64(2) _, err = apiClient.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) require.NoError(t, err) t.Log("Recover called") @@ -176,7 +176,7 @@ func TestWalletBalance(t *testing.T) { func TestWalletSignMessage(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients | osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -190,7 +190,7 @@ func TestWalletSignMessage(t *testing.T) { } // Regen the wallet - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() derivationPath := string(wallet.DerivationPath_Default) index := uint64(0) _, err = apiClient.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) @@ -221,7 +221,7 @@ func TestWalletSignMessage(t *testing.T) { func TestWalletSend_EthSuccess(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients | osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -235,7 +235,7 @@ func TestWalletSend_EthSuccess(t *testing.T) { } // Regen the wallet - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() derivationPath := string(wallet.DerivationPath_Default) index := uint64(0) _, err = apiClient.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) @@ -268,7 +268,7 @@ func TestWalletSend_EthSuccess(t *testing.T) { t.Log("Waiting complete") // Check the balance - sp := testMgr.GetServiceProvider() + sp := hdNode.GetServiceProvider() ctx := sp.GetBaseContext() ecManager := sp.GetEthClient() @@ -285,7 +285,7 @@ func TestWalletSend_EthSuccess(t *testing.T) { func TestWalletSend_EthFailure(t *testing.T) { // Take a snapshot, revert at the end - snapshotName, err := testMgr.CreateCustomSnapshot(osha.Service_EthClients | osha.Service_Filesystem) + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem) if err != nil { fail("Error creating custom snapshot: %v", err) } @@ -299,7 +299,7 @@ func TestWalletSend_EthFailure(t *testing.T) { } // Regen the wallet - apiClient := testMgr.GetApiClient() + apiClient := hdNode.GetApiClient() derivationPath := string(wallet.DerivationPath_Default) index := uint64(0) _, err = apiClient.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, goodPassword, true) @@ -336,7 +336,7 @@ func wallet_cleanup(snapshotName string) { } // Reload the wallet to undo any changes made during the test - err = testMgr.GetServiceProvider().GetWallet().Reload(testMgr.GetLogger()) + err = hdNode.GetServiceProvider().GetWallet().Reload(testMgr.GetLogger()) if err != nil { fail("Error reloading wallet: %v", err) } diff --git a/internal/tests/api/with-ns-registered/constellation_test.go b/internal/tests/api/with-ns-registered/constellation_test.go new file mode 100644 index 0000000..604fcb9 --- /dev/null +++ b/internal/tests/api/with-ns-registered/constellation_test.go @@ -0,0 +1,75 @@ +package with_ns_registered + +import ( + "math/big" + "runtime/debug" + "testing" + + "github.com/ethereum/go-ethereum/common" + hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/nodeset-org/osha/keys" + "github.com/rocket-pool/node-manager-core/utils" + "github.com/stretchr/testify/require" +) + +const ( + whitelistAddressString string = "0xA9e6Bfa2BF53dE88FEb19761D9b2eE2e821bF1Bf" + expectedWhitelistSignature string = "0xf2b73cd729a9b15e8f17ce0189c4ddfe63ad35917f63e2b1ffa7ea1dc527bdf535ba05ba44d2dce733096b8c389472e81a4548b1d75a600633c4ac4bcb8e7c6f1b" + expectedMinipoolCount int = 10 +) + +// Test getting a signature for whitelisting a node +func TestConstellationWhitelistSignature(t *testing.T) { + // Take a snapshot, revert at the end + snapshotName, err := testMgr.CreateCustomSnapshot(hdtesting.Service_EthClients | hdtesting.Service_Filesystem | hdtesting.Service_NodeSet) + if err != nil { + fail("Error creating custom snapshot: %v", err) + } + defer nodeset_cleanup(snapshotName) + + // Get the private key for the Constellation deployer (the admin) + keygen, err := keys.NewKeyGeneratorWithDefaults() + require.NoError(t, err) + adminKey, err := keygen.GetEthPrivateKey(0) + require.NoError(t, err) + + // Set up the nodeset.io mock + res := testMgr.GetNode().GetServiceProvider().GetResources() + nsMgr := testMgr.GetNodeSetMockServer().GetManager() + nsDB := nsMgr.GetDatabase() + deployment := nsDB.Constellation.AddDeployment( + res.DeploymentName, + new(big.Int).SetUint64(uint64(res.ChainID)), + common.HexToAddress(whitelistAddressString), + common.Address{}, + ) + deployment.SetAdminPrivateKey(adminKey) + + // Get a whitelist signature + hd := hdNode.GetApiClient() + response, err := hd.NodeSet_Constellation.GetRegistrationSignature() + require.NoError(t, err) + require.False(t, response.Data.NotAuthorized) + require.False(t, response.Data.NotRegistered) + sigHex := utils.EncodeHexWithPrefix(response.Data.Signature) + require.Equal(t, expectedWhitelistSignature, sigHex) + t.Logf("Whitelist signature is correct") +} + +// Cleanup after a unit test +func nodeset_cleanup(snapshotName string) { + // Handle panics + r := recover() + if r != nil { + debug.PrintStack() + fail("Recovered from panic: %v", r) + } + + // Revert to the snapshot taken at the start of the test + if snapshotName != "" { + err := testMgr.RevertToCustomSnapshot(snapshotName) + if err != nil { + fail("Error reverting to custom snapshot: %v", err) + } + } +} diff --git a/internal/tests/api/with-ns-registered/main_test.go b/internal/tests/api/with-ns-registered/main_test.go new file mode 100644 index 0000000..9187bc6 --- /dev/null +++ b/internal/tests/api/with-ns-registered/main_test.go @@ -0,0 +1,96 @@ +package with_ns_registered + +import ( + "fmt" + "log/slog" + "os" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + hdtesting "github.com/nodeset-org/hyperdrive-daemon/testing" + "github.com/nodeset-org/osha/keys" + "github.com/rocket-pool/node-manager-core/config" + "github.com/rocket-pool/node-manager-core/log" + "github.com/rocket-pool/node-manager-core/wallet" +) + +// Various singleton variables used for testing +var ( + testMgr *hdtesting.HyperdriveTestManager = nil + wg *sync.WaitGroup = nil + logger *slog.Logger = nil + nodeAddress common.Address + nsEmail string = "test@nodeset.io" + hdNode *hdtesting.HyperdriveNode +) + +// Initialize a common server used by all tests +func TestMain(m *testing.M) { + wg = &sync.WaitGroup{} + var err error + testMgr, err = hdtesting.NewHyperdriveTestManagerWithDefaults(func(ns *config.NetworkSettings) *config.NetworkSettings { + return ns + }) + if err != nil { + fail("error creating test manager: %v", err) + } + logger = testMgr.GetLogger() + + // Generate a new wallet + derivationPath := string(wallet.DerivationPath_Default) + index := uint64(4) + password := "test_password123" + hdNode = testMgr.GetNode() + hd := hdNode.GetApiClient() + recoverResponse, err := hd.Wallet.Recover(&derivationPath, keys.DefaultMnemonic, &index, password, true) + if err != nil { + fail("error generating wallet: %v", err) + } + nodeAddress = recoverResponse.Data.AccountAddress + + // Make a NodeSet account + nsServer := testMgr.GetNodeSetMockServer().GetManager() + nsDB := nsServer.GetDatabase() + user, err := nsDB.Core.AddUser(nsEmail) + if err != nil { + fail("error adding user to nodeset: %v", err) + } + user.WhitelistNode(nodeAddress) + + // Register with NodeSet + response, err := hd.NodeSet.RegisterNode(nsEmail) + if err != nil { + fail("error registering node with nodeset: %v", err) + } + if response.Data.AlreadyRegistered { + fail("node is already registered with nodeset") + } + if response.Data.NotWhitelisted { + fail("node is not whitelisted with a nodeset user account") + } + + // Run tests + code := m.Run() + + // Clean up and exit + cleanup() + os.Exit(code) +} + +func fail(format string, args ...any) { + fmt.Fprintf(os.Stderr, format, args...) + cleanup() + os.Exit(1) +} + +func cleanup() { + if testMgr == nil { + return + } + err := testMgr.Close() + if err != nil { + logger.Error("Error closing test manager", log.Err(err)) + } + testMgr = nil +} diff --git a/module-utils/gas/utils.go b/module-utils/gas/utils.go new file mode 100644 index 0000000..f619399 --- /dev/null +++ b/module-utils/gas/utils.go @@ -0,0 +1,97 @@ +package gas + +import ( + "fmt" + "log/slog" + "math/big" + + "github.com/rocket-pool/node-manager-core/eth" + "github.com/rocket-pool/node-manager-core/gas" + "github.com/rocket-pool/node-manager-core/log" + "github.com/rocket-pool/node-manager-core/utils/math" +) + +const ( + CurrentMaxFeeKey string = "currentMaxFee" + ThresholdMaxFeeKey string = "thresholdMaxFee" + MinCostKey string = "minCost" + MaxCostKey string = "maxCost" + TotalMinCostKey string = "totalMinCost" + TotalMaxCostKey string = "totalMaxCost" +) + +// Print the gas price and cost of a TX +func PrintAndCheckGasInfo(simResult eth.SimulationResult, checkThreshold bool, gasThresholdGwei float64, logger *slog.Logger, maxFeeWei *big.Int, gasLimit uint64) bool { + // Check the gas threshold if requested + if checkThreshold { + gasThresholdWei := math.RoundUp(gasThresholdGwei*eth.WeiPerGwei, 0) + gasThreshold := new(big.Int).SetUint64(uint64(gasThresholdWei)) + if maxFeeWei.Cmp(gasThreshold) != -1 { + logger.Warn("Current network gas price is too high, aborting the transaction.", slog.Float64(CurrentMaxFeeKey, eth.WeiToGwei(maxFeeWei)), slog.Float64(ThresholdMaxFeeKey, gasThresholdGwei)) + return false + } + } else { + logger.Info("This transaction does not check the gas threshold limit, continuing...") + } + + // Print the total TX cost + var gas *big.Int + var safeGas *big.Int + if gasLimit != 0 { + gas = new(big.Int).SetUint64(gasLimit) + safeGas = gas + } else { + gas = new(big.Int).SetUint64(simResult.EstimatedGasLimit) + safeGas = new(big.Int).SetUint64(simResult.SafeGasLimit) + } + totalGasWei := new(big.Int).Mul(maxFeeWei, gas) + totalSafeGasWei := new(big.Int).Mul(maxFeeWei, safeGas) + logger.Info("Current network gas is low enough to proceed", slog.Float64(CurrentMaxFeeKey, eth.WeiToGwei(maxFeeWei)), slog.Float64(MinCostKey, eth.WeiToEth(totalGasWei)), slog.Float64(MaxCostKey, eth.WeiToEth(totalSafeGasWei))) + return true +} + +// Print the gas price and cost of a TX batch +func PrintAndCheckGasInfoForBatch(submissions []*eth.TransactionSubmission, checkThreshold bool, gasThresholdGwei float64, logger *slog.Logger, maxFeeWei *big.Int) bool { + // Check the gas threshold if requested + if checkThreshold { + gasThresholdWei := math.RoundUp(gasThresholdGwei*eth.WeiPerGwei, 0) + gasThreshold := new(big.Int).SetUint64(uint64(gasThresholdWei)) + if maxFeeWei.Cmp(gasThreshold) != -1 { + logger.Warn("Current network gas price is too high, aborting the transaction.", slog.Float64(CurrentMaxFeeKey, eth.WeiToGwei(maxFeeWei)), slog.Float64(ThresholdMaxFeeKey, gasThresholdGwei)) + return false + } + } else { + logger.Info("This transaction does not check the gas threshold limit, continuing...") + } + + // Print the total TX cost + totalEstGasWei := big.NewInt(0) + totalAssignedGasWei := big.NewInt(0) + for _, submission := range submissions { + lowGas := big.NewInt(0).SetUint64(submission.TxInfo.SimulationResult.EstimatedGasLimit) + highGas := big.NewInt(0).SetUint64(submission.GasLimit) + lowGas.Mul(lowGas, maxFeeWei) + highGas.Mul(highGas, maxFeeWei) + totalEstGasWei.Add(totalEstGasWei, lowGas) + totalAssignedGasWei.Add(totalAssignedGasWei, highGas) + } + + logger.Info("Current network gas is low enough to proceed", slog.Float64(CurrentMaxFeeKey, eth.WeiToGwei(maxFeeWei)), slog.Float64(TotalMinCostKey, eth.WeiToEth(totalEstGasWei)), slog.Float64(TotalMaxCostKey, eth.WeiToEth(totalAssignedGasWei))) + return true +} + +// Get the suggested max fee for service operations +func GetMaxFeeWeiForDaemon(logger *slog.Logger) (*big.Int, error) { + etherchainData, err := gas.GetEtherchainGasPrices() + if err == nil { + return etherchainData.RapidWei, nil + } + + logger.Warn("Couldn't get gas estimates from Etherchain, falling back to Etherscan\n", log.Err(err)) + etherscanData, err := gas.GetEtherscanGasPrices() + if err == nil { + return eth.GweiToWei(etherscanData.FastGwei), nil + } + + return nil, fmt.Errorf("error getting gas price suggestions: %w", err) +} diff --git a/module-utils/server/queryless.go b/module-utils/server/queryless.go index b250180..76b66d3 100644 --- a/module-utils/server/queryless.go +++ b/module-utils/server/queryless.go @@ -48,7 +48,7 @@ func RegisterQuerylessGet[ContextType IQuerylessCallContext[DataType], DataType functionName string, factory IQuerylessGetContextFactory[ContextType, DataType], logger *slog.Logger, - serviceProvider *services.ServiceProvider, + serviceProvider services.IModuleServiceProvider, ) { router.HandleFunc(fmt.Sprintf("/%s", functionName), func(w http.ResponseWriter, r *http.Request) { // Log @@ -91,7 +91,7 @@ func RegisterQuerylessPost[ContextType IQuerylessCallContext[DataType], BodyType functionName string, factory IQuerylessPostContextFactory[ContextType, BodyType, DataType], logger *slog.Logger, - serviceProvider *services.ServiceProvider, + serviceProvider services.IModuleServiceProvider, ) { router.HandleFunc(fmt.Sprintf("/%s", functionName), func(w http.ResponseWriter, r *http.Request) { // Log @@ -148,7 +148,7 @@ func RegisterQuerylessPost[ContextType IQuerylessCallContext[DataType], BodyType } // Run a route registered with no structured chain query pattern -func runQuerylessRoute[DataType any](ctx IQuerylessCallContext[DataType], serviceProvider *services.ServiceProvider) (types.ResponseStatus, *types.ApiResponse[DataType], error) { +func runQuerylessRoute[DataType any](ctx IQuerylessCallContext[DataType], serviceProvider services.IModuleServiceProvider) (types.ResponseStatus, *types.ApiResponse[DataType], error) { // Get the services hd := serviceProvider.GetHyperdriveClient() signer := serviceProvider.GetSigner() diff --git a/module-utils/server/single-stage.go b/module-utils/server/single-stage.go index 7afef22..43ec629 100644 --- a/module-utils/server/single-stage.go +++ b/module-utils/server/single-stage.go @@ -53,7 +53,7 @@ func RegisterSingleStageRoute[ContextType ISingleStageCallContext[DataType], Dat functionName string, factory ISingleStageGetContextFactory[ContextType, DataType], logger *slog.Logger, - serviceProvider *services.ServiceProvider, + serviceProvider services.IModuleServiceProvider, ) { router.HandleFunc(fmt.Sprintf("/%s", functionName), func(w http.ResponseWriter, r *http.Request) { // Log @@ -96,7 +96,7 @@ func RegisterSingleStagePost[ContextType ISingleStageCallContext[DataType], Body functionName string, factory ISingleStagePostContextFactory[ContextType, BodyType, DataType], logger *slog.Logger, - serviceProvider *services.ServiceProvider, + serviceProvider services.IModuleServiceProvider, ) { router.HandleFunc(fmt.Sprintf("/%s", functionName), func(w http.ResponseWriter, r *http.Request) { // Log @@ -153,7 +153,7 @@ func RegisterSingleStagePost[ContextType ISingleStageCallContext[DataType], Body } // Run a route registered with the common single-stage querying pattern -func runSingleStageRoute[DataType any](ctx ISingleStageCallContext[DataType], serviceProvider *services.ServiceProvider) (types.ResponseStatus, *types.ApiResponse[DataType], error) { +func runSingleStageRoute[DataType any](ctx ISingleStageCallContext[DataType], serviceProvider services.IModuleServiceProvider) (types.ResponseStatus, *types.ApiResponse[DataType], error) { // Get the services q := serviceProvider.GetQueryManager() hd := serviceProvider.GetHyperdriveClient() diff --git a/module-utils/services/requirements.go b/module-utils/services/requirements.go index bee4aa0..f67b890 100644 --- a/module-utils/services/requirements.go +++ b/module-utils/services/requirements.go @@ -31,31 +31,24 @@ const ( ) var ( - //lint:ignore ST1005 These are printed to the user and need to be in proper grammatical format ErrExecutionClientNotSynced error = errors.New("The Execution client is currently syncing. Please try again later.") - - //lint:ignore ST1005 These are printed to the user and need to be in proper grammatical format - ErrBeaconNodeNotSynced error = errors.New("The Beacon node is currently syncing. Please try again later.") - - //lint:ignore ST1005 These are printed to the user and need to be in proper grammatical format + ErrBeaconNodeNotSynced error = errors.New("The Beacon node is currently syncing. Please try again later.") ErrNotRegisteredWithNodeSet error = errors.New("The node is not registered with the Node Set. Please run 'hyperdrive nodeset register-node' and try again.") - - //lint:ignore ST1005 These are printed to the user and need to be in proper grammatical format - ErrWalletNotReady error = errors.New("The node does not have a wallet ready yet. Please run 'hyperdrive wallet status' to learn more first.") + ErrWalletNotReady error = errors.New("The node does not have a wallet ready yet. Please run 'hyperdrive wallet status' to learn more first.") ) -func (sp *ServiceProvider) RequireNodeAddress(status wallet.WalletStatus) error { +func (sp *moduleServiceProvider) RequireNodeAddress(status wallet.WalletStatus) error { if !status.Address.HasAddress { - return errors.New("The node currently does not have an address set. Please run 'hyperdrive wallet init' and try again.") + return ErrNodeAddressNotSet } return nil } -func (sp *ServiceProvider) RequireWalletReady(status wallet.WalletStatus) error { +func (sp *moduleServiceProvider) RequireWalletReady(status wallet.WalletStatus) error { return CheckIfWalletReady(status) } -func (sp *ServiceProvider) RequireEthClientSynced(ctx context.Context) error { +func (sp *moduleServiceProvider) RequireEthClientSynced(ctx context.Context) error { synced, _, err := sp.checkExecutionClientStatus(ctx) if err != nil { return err @@ -66,7 +59,7 @@ func (sp *ServiceProvider) RequireEthClientSynced(ctx context.Context) error { return ErrExecutionClientNotSynced } -func (sp *ServiceProvider) RequireBeaconClientSynced(ctx context.Context) error { +func (sp *moduleServiceProvider) RequireBeaconClientSynced(ctx context.Context) error { synced, err := sp.checkBeaconClientStatus(ctx) if err != nil { return err @@ -77,7 +70,7 @@ func (sp *ServiceProvider) RequireBeaconClientSynced(ctx context.Context) error return ErrBeaconNodeNotSynced } -func (sp *ServiceProvider) RequireRegisteredWithNodeSet(ctx context.Context) error { +func (sp *moduleServiceProvider) RequireRegisteredWithNodeSet(ctx context.Context) error { response, err := sp.hdClient.NodeSet.GetRegistrationStatus() if err != nil { return err @@ -94,19 +87,47 @@ func (sp *ServiceProvider) RequireRegisteredWithNodeSet(ctx context.Context) err } // Wait for the Executon client to sync; timeout of 0 indicates no timeout -func (sp *ServiceProvider) WaitEthClientSynced(ctx context.Context, verbose bool) error { +func (sp *moduleServiceProvider) WaitEthClientSynced(ctx context.Context, verbose bool) error { _, err := sp.waitEthClientSynced(ctx, verbose) return err } // Wait for the Beacon client to sync; timeout of 0 indicates no timeout -func (sp *ServiceProvider) WaitBeaconClientSynced(ctx context.Context, verbose bool) error { +func (sp *moduleServiceProvider) WaitBeaconClientSynced(ctx context.Context, verbose bool) error { _, err := sp.waitBeaconClientSynced(ctx, verbose) return err } +// Wait for Hyperdrive to have a node address assigned +func (sp *moduleServiceProvider) WaitForNodeAddress(ctx context.Context) (*wallet.WalletStatus, error) { + // Get the logger + logger, exists := log.FromContext(ctx) + if !exists { + panic("context didn't have a logger!") + } + + for { + hdWalletStatus, err := sp.GetHyperdriveClient().Wallet.Status() + if err != nil { + return nil, fmt.Errorf("error getting Hyperdrive wallet status: %w", err) + } + + status := hdWalletStatus.Data.WalletStatus + if status.Address.HasAddress { + return &status, nil + } + + logger.Info("Node address not present yet", + slog.Duration("retry", walletReadyCheckInterval), + ) + if utils.SleepWithCancel(ctx, walletReadyCheckInterval) { + return nil, nil + } + } +} + // Wait for the Hyperdrive wallet to be ready -func (sp *ServiceProvider) WaitForWallet(ctx context.Context) error { +func (sp *moduleServiceProvider) WaitForWallet(ctx context.Context) (*wallet.WalletStatus, error) { // Get the logger logger, exists := log.FromContext(ctx) if !exists { @@ -116,25 +137,25 @@ func (sp *ServiceProvider) WaitForWallet(ctx context.Context) error { for { hdWalletStatus, err := sp.GetHyperdriveClient().Wallet.Status() if err != nil { - return fmt.Errorf("error getting Hyperdrive wallet status: %w", err) + return nil, fmt.Errorf("error getting Hyperdrive wallet status: %w", err) } if CheckIfWalletReady(hdWalletStatus.Data.WalletStatus) == nil { - return nil + return &hdWalletStatus.Data.WalletStatus, nil } logger.Info("Hyperdrive wallet not ready yet", slog.Duration("retry", walletReadyCheckInterval), ) if utils.SleepWithCancel(ctx, walletReadyCheckInterval) { - return nil + return nil, nil } } } // Wait until the node has been registered with NodeSet. // Returns true if the context was cancelled and the caller should exit. -func (sp *ServiceProvider) WaitForNodeSetRegistration(ctx context.Context) bool { +func (sp *moduleServiceProvider) WaitForNodeSetRegistration(ctx context.Context) bool { // Get the logger logger, exists := log.FromContext(ctx) if !exists { @@ -172,7 +193,7 @@ func (sp *ServiceProvider) WaitForNodeSetRegistration(ctx context.Context) bool // Check if the primary and fallback Execution clients are synced // TODO: Move this into ec-manager and stop exposing the primary and fallback directly... -func (sp *ServiceProvider) checkExecutionClientStatus(ctx context.Context) (bool, eth.IExecutionClient, error) { +func (sp *moduleServiceProvider) checkExecutionClientStatus(ctx context.Context) (bool, eth.IExecutionClient, error) { // Check the EC status ecMgr := sp.GetEthClient() mgrStatus := ecMgr.CheckStatus(ctx, true) // Always check the chain ID for now @@ -219,7 +240,7 @@ func (sp *ServiceProvider) checkExecutionClientStatus(ctx context.Context) (bool } // Check if the primary and fallback Beacon clients are synced -func (sp *ServiceProvider) checkBeaconClientStatus(ctx context.Context) (bool, error) { +func (sp *moduleServiceProvider) checkBeaconClientStatus(ctx context.Context) (bool, error) { // Check the BC status bcMgr := sp.GetBeaconClient() mgrStatus := bcMgr.CheckStatus(ctx, true) // Always check the chain ID for now @@ -266,7 +287,7 @@ func (sp *ServiceProvider) checkBeaconClientStatus(ctx context.Context) (bool, e } // Wait for the primary or fallback Execution client to be synced -func (sp *ServiceProvider) waitEthClientSynced(ctx context.Context, verbose bool) (bool, error) { +func (sp *moduleServiceProvider) waitEthClientSynced(ctx context.Context, verbose bool) (bool, error) { synced, clientToCheck, err := sp.checkExecutionClientStatus(ctx) if err != nil { return false, err @@ -334,7 +355,7 @@ func (sp *ServiceProvider) waitEthClientSynced(ctx context.Context, verbose bool } // Wait for the primary or fallback Beacon client to be synced -func (sp *ServiceProvider) waitBeaconClientSynced(ctx context.Context, verbose bool) (bool, error) { +func (sp *moduleServiceProvider) waitBeaconClientSynced(ctx context.Context, verbose bool) (bool, error) { synced, err := sp.checkBeaconClientStatus(ctx) if err != nil { return false, err diff --git a/module-utils/services/service-provider.go b/module-utils/services/service-provider.go index 345e4eb..c6a52db 100644 --- a/module-utils/services/service-provider.go +++ b/module-utils/services/service-provider.go @@ -3,6 +3,7 @@ package services import ( "context" "fmt" + "io" "log/slog" "net/url" "reflect" @@ -18,17 +19,109 @@ import ( "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/node-manager-core/log" "github.com/rocket-pool/node-manager-core/node/services" + "github.com/rocket-pool/node-manager-core/wallet" ) +// ================== +// === Interfaces === +// ================== + +// Provides the configurations for Hyperdrive and the module +type IModuleConfigProvider interface { + // Gets Hyperdrive's configuration + GetHyperdriveConfig() *hdconfig.HyperdriveConfig + + // Gets Hyperdrive's list of resources + GetHyperdriveResources() *hdconfig.MergedResources + + // Gets the module's configuration + GetModuleConfig() hdconfig.IModuleConfig + + // Gets the path to the module's data directory + GetModuleDir() string +} + +// Provides a Hyperdrive API client +type IHyperdriveClientProvider interface { + // Gets the Hyperdrive client + GetHyperdriveClient() *client.ApiClient +} + +// Provides a signer that can sign messages from the node's wallet +type ISignerProvider interface { + // Gets the module's signer + GetSigner() *ModuleSigner +} + +// Provides access to the module's loggers +type ILoggerProvider interface { + services.ILoggerProvider + + // Gets the logger for the Hyperdrive client + GetClientLogger() *log.Logger +} + +// Provides methods for requiring or waiting for various conditions to be met +type IRequirementsProvider interface { + // Require Hyperdrive has a node address set + RequireNodeAddress(status wallet.WalletStatus) error + + // Require Hyperdrive has a wallet that's loaded and ready for transactions + RequireWalletReady(status wallet.WalletStatus) error + + // Require that the Ethereum client is synced + RequireEthClientSynced(ctx context.Context) error + + // Require that the Beacon chain client is synced + RequireBeaconClientSynced(ctx context.Context) error + + // Require the node has been registered with a nodeset.io account + RequireRegisteredWithNodeSet(ctx context.Context) error + + // Wait for the Ethereum client to be synced + WaitEthClientSynced(ctx context.Context, verbose bool) error + + // Wait for the Beacon chain client to be synced + WaitBeaconClientSynced(ctx context.Context, verbose bool) error + + // Wait for Hyperdrive to have a node address assigned + WaitForNodeAddress(ctx context.Context) (*wallet.WalletStatus, error) + + // Wait for the node to have a wallet loaded and ready for transactions + WaitForWallet(ctx context.Context) (*wallet.WalletStatus, error) + + // Wait for the node to be registered with a nodeset.io account + WaitForNodeSetRegistration(ctx context.Context) bool +} + +// Provides access to all of the standard services the module can use +type IModuleServiceProvider interface { + IModuleConfigProvider + IHyperdriveClientProvider + ISignerProvider + ILoggerProvider + IRequirementsProvider + + // Standard NMC interfaces + services.IEthClientProvider + services.IBeaconClientProvider + services.IContextProvider + io.Closer +} + +// ======================== +// === Service Provider === +// ======================== + // A container for all of the various services used by Hyperdrive -type ServiceProvider struct { +type moduleServiceProvider struct { // Services hdCfg *hdconfig.HyperdriveConfig moduleConfig hdconfig.IModuleConfig hdClient *client.ApiClient ecManager *services.ExecutionClientManager bcManager *services.BeaconClientManager - resources *hdconfig.HyperdriveResources + resources *hdconfig.MergedResources signer *ModuleSigner txMgr *eth.TransactionManager queryMgr *eth.QueryManager @@ -45,16 +138,13 @@ type ServiceProvider struct { userDir string } -// Creates a new ServiceProvider instance -func NewServiceProvider[ConfigType hdconfig.IModuleConfig](hyperdriveUrl *url.URL, moduleDir string, moduleName string, clientLogName string, factory func(*hdconfig.HyperdriveConfig) ConfigType, clientTimeout time.Duration) (*ServiceProvider, error) { - hdCfg, hdClient, err := getHdConfig(hyperdriveUrl) +// Creates a new IModuleServiceProvider instance +func NewModuleServiceProvider[ConfigType hdconfig.IModuleConfig](hyperdriveUrl *url.URL, moduleDir string, moduleName string, clientLogName string, factory func(*hdconfig.HyperdriveConfig) (ConfigType, error), clientTimeout time.Duration) (IModuleServiceProvider, error) { + hdCfg, resources, hdClient, err := getHdConfig(hyperdriveUrl) if err != nil { return nil, fmt.Errorf("error getting Hyperdrive config: %w", err) } - // Resources - resources := hdCfg.GetResources() - // EC Manager var ecManager *services.ExecutionClientManager primaryEcUrl, fallbackEcUrl := hdCfg.GetExecutionClientUrls() @@ -85,7 +175,10 @@ func NewServiceProvider[ConfigType hdconfig.IModuleConfig](hyperdriveUrl *url.UR } // Get the module config - moduleCfg := factory(hdCfg) + moduleCfg, err := factory(hdCfg) + if err != nil { + return nil, fmt.Errorf("error creating module config: %w", err) + } modCfgEnrty, exists := hdCfg.Modules[moduleName] if !exists { return nil, fmt.Errorf("config section for module [%s] not found", moduleName) @@ -99,11 +192,11 @@ func NewServiceProvider[ConfigType hdconfig.IModuleConfig](hyperdriveUrl *url.UR return nil, fmt.Errorf("error deserialzing config for module [%s]: %w", moduleName, err) } - return NewServiceProviderFromArtifacts(hdClient, hdCfg, moduleCfg, resources, moduleDir, moduleName, clientLogName, ecManager, bcManager) + return NewModuleServiceProviderFromArtifacts(hdClient, hdCfg, moduleCfg, resources, moduleDir, moduleName, clientLogName, ecManager, bcManager) } -// Creates a new ServiceProvider instance, using the given artifacts instead of creating ones based on the config parameters -func NewServiceProviderFromArtifacts(hdClient *client.ApiClient, hdCfg *hdconfig.HyperdriveConfig, moduleCfg hdconfig.IModuleConfig, resources *hdconfig.HyperdriveResources, moduleDir string, moduleName string, clientLogName string, ecManager *services.ExecutionClientManager, bcManager *services.BeaconClientManager) (*ServiceProvider, error) { +// Creates a new IModuleServiceProvider instance, using the given artifacts instead of creating ones based on the config parameters +func NewModuleServiceProviderFromArtifacts(hdClient *client.ApiClient, hdCfg *hdconfig.HyperdriveConfig, moduleCfg hdconfig.IModuleConfig, resources *hdconfig.MergedResources, moduleDir string, moduleName string, clientLogName string, ecManager *services.ExecutionClientManager, bcManager *services.BeaconClientManager) (IModuleServiceProvider, error) { // Set up the client logger logPath := hdCfg.GetModuleLogFilePath(moduleName, clientLogName) clientLogger, err := log.NewLogger(logPath, hdCfg.GetLoggerOptions()) @@ -151,7 +244,7 @@ func NewServiceProviderFromArtifacts(hdClient *client.ApiClient, hdCfg *hdconfig tasksLogger.Info("Starting Tasks logger.") // Create the provider - provider := &ServiceProvider{ + provider := &moduleServiceProvider{ moduleDir: moduleDir, userDir: hdCfg.GetUserDirectory(), hdCfg: hdCfg, @@ -173,77 +266,74 @@ func NewServiceProviderFromArtifacts(hdClient *client.ApiClient, hdCfg *hdconfig } // Closes the service provider and its underlying services -func (p *ServiceProvider) Close() { +func (p *moduleServiceProvider) Close() error { p.clientLogger.Close() p.apiLogger.Close() p.tasksLogger.Close() + return nil } // =============== // === Getters === // =============== -func (p *ServiceProvider) GetModuleDir() string { +func (p *moduleServiceProvider) GetModuleDir() string { return p.moduleDir } -func (p *ServiceProvider) GetUserDir() string { - return p.userDir +func (p *moduleServiceProvider) GetHyperdriveConfig() *hdconfig.HyperdriveConfig { + return p.hdCfg } -func (p *ServiceProvider) GetHyperdriveConfig() *hdconfig.HyperdriveConfig { - return p.hdCfg +func (p *moduleServiceProvider) GetHyperdriveResources() *hdconfig.MergedResources { + return p.resources } -func (p *ServiceProvider) GetModuleConfig() hdconfig.IModuleConfig { +func (p *moduleServiceProvider) GetModuleConfig() hdconfig.IModuleConfig { return p.moduleConfig } -func (p *ServiceProvider) GetEthClient() *services.ExecutionClientManager { +func (p *moduleServiceProvider) GetEthClient() *services.ExecutionClientManager { return p.ecManager } -func (p *ServiceProvider) GetBeaconClient() *services.BeaconClientManager { +func (p *moduleServiceProvider) GetBeaconClient() *services.BeaconClientManager { return p.bcManager } -func (p *ServiceProvider) GetHyperdriveClient() *client.ApiClient { +func (p *moduleServiceProvider) GetHyperdriveClient() *client.ApiClient { return p.hdClient } -func (p *ServiceProvider) GetResources() *hdconfig.HyperdriveResources { - return p.resources -} - -func (p *ServiceProvider) GetSigner() *ModuleSigner { +func (p *moduleServiceProvider) GetSigner() *ModuleSigner { return p.signer } -func (p *ServiceProvider) GetTransactionManager() *eth.TransactionManager { +func (p *moduleServiceProvider) GetTransactionManager() *eth.TransactionManager { return p.txMgr } -func (p *ServiceProvider) GetQueryManager() *eth.QueryManager { +func (p *moduleServiceProvider) GetQueryManager() *eth.QueryManager { return p.queryMgr } -func (p *ServiceProvider) GetClientLogger() *log.Logger { +func (p *moduleServiceProvider) GetClientLogger() *log.Logger { return p.clientLogger } -func (p *ServiceProvider) GetApiLogger() *log.Logger { +func (p *moduleServiceProvider) GetApiLogger() *log.Logger { return p.apiLogger } -func (p *ServiceProvider) GetTasksLogger() *log.Logger { +func (p *moduleServiceProvider) GetTasksLogger() *log.Logger { return p.tasksLogger } -func (p *ServiceProvider) GetBaseContext() context.Context { +func (p *moduleServiceProvider) GetBaseContext() context.Context { return p.ctx } -func (p *ServiceProvider) CancelContextOnShutdown() { +func (p *moduleServiceProvider) CancelContextOnShutdown() { p.cancel() } @@ -251,7 +341,7 @@ func (p *ServiceProvider) CancelContextOnShutdown() { // === Internal Functions === // ========================== -func getHdConfig(hyperdriveUrl *url.URL) (*hdconfig.HyperdriveConfig, *client.ApiClient, error) { +func getHdConfig(hyperdriveUrl *url.URL) (*hdconfig.HyperdriveConfig, *hdconfig.MergedResources, *client.ApiClient, error) { // Add the API client route if missing hyperdriveUrl.Path = strings.TrimSuffix(hyperdriveUrl.Path, "/") if hyperdriveUrl.Path == "" { @@ -262,16 +352,30 @@ func getHdConfig(hyperdriveUrl *url.URL) (*hdconfig.HyperdriveConfig, *client.Ap defaultLogger := slog.Default() hdClient := client.NewApiClient(hyperdriveUrl, defaultLogger, nil) - // Get the Hyperdrive config - hdCfg := hdconfig.NewHyperdriveConfig("") + // Get the Hyperdrive settings for the selected network + settingsResponse, err := hdClient.Service.GetNetworkSettings() + if err != nil { + return nil, nil, nil, fmt.Errorf("error getting resources from Hyperdrive server: %w", err) + } + settings := settingsResponse.Data.Settings + + // Create the Hyperdrive network + hdCfg, err := hdconfig.NewHyperdriveConfig("", []*hdconfig.HyperdriveSettings{settings}) + if err != nil { + return nil, nil, nil, fmt.Errorf("error creating Hyperdrive config: %w", err) + } cfgResponse, err := hdClient.Service.GetConfig() if err != nil { - return nil, nil, fmt.Errorf("error getting config from Hyperdrive server: %w", err) + return nil, nil, nil, fmt.Errorf("error getting config from Hyperdrive server: %w", err) } err = hdCfg.Deserialize(cfgResponse.Data.Config) if err != nil { - return nil, nil, fmt.Errorf("error deserializing Hyperdrive config: %w", err) + return nil, nil, nil, fmt.Errorf("error deserializing Hyperdrive config: %w", err) } - return hdCfg, hdClient, nil + res := &hdconfig.MergedResources{ + NetworkResources: settings.NetworkResources, + HyperdriveResources: settings.HyperdriveResources, + } + return hdCfg, res, hdClient, nil } diff --git a/module-utils/services/utils.go b/module-utils/services/utils.go index da52bfa..3c69928 100644 --- a/module-utils/services/utils.go +++ b/module-utils/services/utils.go @@ -6,21 +6,29 @@ import ( "github.com/rocket-pool/node-manager-core/wallet" ) +var ( + ErrNodeAddressNotSet error = errors.New("The node currently does not have an address set. Please run 'hyperdrive wallet init' and try again.") + ErrNeedPassword error = errors.New("The node has a node wallet on disk but does not have the password for it loaded. Please run `hyperdrive wallet set-password` to load it.") + ErrWalletLoadFailure error = errors.New("The node has a node wallet and a password on disk but there was an error loading it - perhaps the password is incorrect? Please check the node logs for more information.") + ErrNoWallet error = errors.New("The node currently does not have a node wallet keystore. Please run 'hyperdrive wallet init' and try again.") + ErrWalletMismatch error = errors.New("The node's wallet keystore does not match the node address. This node is currently in read-only mode.") +) + func CheckIfWalletReady(status wallet.WalletStatus) error { if !status.Address.HasAddress { - return errors.New("The node currently does not have an address set. Please run 'hyperdrive wallet init' and try again.") + return ErrNodeAddressNotSet } if !status.Wallet.IsLoaded { if status.Wallet.IsOnDisk { if !status.Password.IsPasswordSaved { - return errors.New("The node has a node wallet on disk but does not have the password for it loaded. Please run `hyperdrive wallet set-password` to load it.") + return ErrNeedPassword } - return errors.New("The node has a node wallet and a password on disk but there was an error loading it - perhaps the password is incorrect? Please check the node logs for more information.") + return ErrWalletLoadFailure } - return errors.New("The node currently does not have a node wallet keystore. Please run 'hyperdrive wallet init' and try again.") + return ErrNoWallet } if status.Wallet.WalletAddress != status.Address.NodeAddress { - return errors.New("The node's wallet keystore does not match the node address. This node is currently in read-only mode.") + return ErrWalletMismatch } return nil } diff --git a/module-utils/tx/tx.go b/module-utils/tx/tx.go new file mode 100644 index 0000000..e7ef7ec --- /dev/null +++ b/module-utils/tx/tx.go @@ -0,0 +1,119 @@ +package tx + +import ( + "fmt" + "log/slog" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" + "github.com/rocket-pool/node-manager-core/config" + "github.com/rocket-pool/node-manager-core/eth" + "golang.org/x/sync/errgroup" +) + +// Prints a TX's details to the logger and waits for it to validated. +func PrintAndWaitForTransaction(res *config.NetworkResources, txMgr *eth.TransactionManager, logger *slog.Logger, txInfo *eth.TransactionInfo, opts *bind.TransactOpts) error { + tx, err := txMgr.ExecuteTransaction(txInfo, opts) + if err != nil { + return fmt.Errorf("error submitting transaction: %w", err) + } + + txWatchUrl := res.TxWatchUrl + hashString := tx.Hash().String() + logger.Info( + "Transaction has been submitted.", + slog.String("hash", hashString), + ) + if txWatchUrl != "" { + logger.Info("You may follow its progress by visiting:") + logger.Info(fmt.Sprintf("%s/%s\n", txWatchUrl, hashString)) + } + + // Wait for the TX to be included in a block + logger.Info("Waiting for the transaction to be validated...") + err = txMgr.WaitForTransaction(tx) + if err != nil { + return fmt.Errorf("error waiting for transaction: %w", err) + } + + return nil +} + +// Prints a TX's details to the logger and waits for it to validated. +func PrintAndWaitForTransactionBatch(res *config.NetworkResources, txMgr *eth.TransactionManager, logger *slog.Logger, submissions []*eth.TransactionSubmission, opts *bind.TransactOpts) error { + txs, err := txMgr.BatchExecuteTransactions(submissions, opts) + if err != nil { + return fmt.Errorf("error submitting transactions: %w", err) + } + + txWatchUrl := res.TxWatchUrl + if txWatchUrl != "" { + logger.Info("Transactions have been submitted. You may follow them progress by visiting:") + for _, tx := range txs { + hashString := tx.Hash().String() + logger.Info(fmt.Sprintf("%s/%s\n", txWatchUrl, hashString)) + } + } else { + logger.Info("Transactions have been submitted with the following hashes:") + for _, tx := range txs { + logger.Info(tx.Hash().String()) + } + + } + + // Wait for the TX to be included in a block + logger.Info("Waiting for the transactions to be validated...") + var wg errgroup.Group + var waitLock sync.Mutex + completeCount := 0 + + for _, tx := range txs { + tx := tx + wg.Go(func() error { + err := txMgr.WaitForTransaction(tx) + if err != nil { + err = fmt.Errorf("error waiting for transaction %s: %w", tx.Hash().String(), err) + } else { + waitLock.Lock() + completeCount++ + logger.Info(fmt.Sprintf("TX %s complete (%d/%d)", tx.Hash().String(), completeCount, len(txs))) + waitLock.Unlock() + } + return err + }) + } + + err = wg.Wait() + if err != nil { + return err + } + + logger.Info("Transaction batch complete.") + return nil +} + +// Gets the automatic TX max fee and max priority fee for daemon processes +func GetAutoTxInfo(cfg *hdconfig.HyperdriveConfig, logger *slog.Logger) (*big.Int, *big.Int) { + // Get the user-requested max fee + maxFeeGwei := cfg.AutoTxMaxFee.Value + var maxFee *big.Int + if maxFeeGwei == 0 { + maxFee = nil + } else { + maxFee = eth.GweiToWei(maxFeeGwei) + } + + // Get the user-requested max fee + priorityFeeGwei := cfg.MaxPriorityFee.Value + var priorityFee *big.Int + if priorityFeeGwei == 0 { + logger.Warn("Priority fee was missing or 0, setting a default of 2.") + priorityFee = eth.GweiToWei(2) + } else { + priorityFee = eth.GweiToWei(priorityFeeGwei) + } + + return maxFee, priorityFee +} diff --git a/server/api/nodeset/constellation/get-deposit-signature.go b/server/api/nodeset/constellation/get-deposit-signature.go new file mode 100644 index 0000000..98ff6d4 --- /dev/null +++ b/server/api/nodeset/constellation/get-deposit-signature.go @@ -0,0 +1,95 @@ +package ns_constellation + +import ( + "errors" + "math/big" + "net/url" + + hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/gorilla/mux" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" + "github.com/rocket-pool/node-manager-core/utils/input" +) + +// =============== +// === Factory === +// =============== + +type constellationGetDepositSignatureContextFactory struct { + handler *ConstellationHandler +} + +func (f *constellationGetDepositSignatureContextFactory) Create(args url.Values) (*constellationGetDepositSignatureContext, error) { + c := &constellationGetDepositSignatureContext{ + handler: f.handler, + } + inputErrs := []error{ + server.ValidateArg("minipoolAddress", args, input.ValidateAddress, &c.minipoolAddress), + server.ValidateArg("salt", args, input.ValidateBigInt, &c.salt), + } + return c, errors.Join(inputErrs...) +} + +func (f *constellationGetDepositSignatureContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*constellationGetDepositSignatureContext, api.NodeSetConstellation_GetDepositSignatureData]( + router, "get-deposit-signature", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== +type constellationGetDepositSignatureContext struct { + handler *ConstellationHandler + + minipoolAddress common.Address + salt *big.Int +} + +func (c *constellationGetDepositSignatureContext) PrepareData(data *api.NodeSetConstellation_GetDepositSignatureData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + ctx := c.handler.ctx + + // Requirements + err := sp.RequireWalletReady() + if err != nil { + return types.ResponseStatus_WalletNotReady, err + } + err = sp.RequireRegisteredWithNodeSet(ctx) + if err != nil { + if errors.Is(err, hdcommon.ErrNotRegisteredWithNodeSet) { + data.NotRegistered = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + // Get the set version + ns := sp.GetNodeSetServiceManager() + signature, err := ns.Constellation_GetDepositSignature(ctx, c.minipoolAddress, c.salt) + if err != nil { + if errors.Is(err, v2constellation.ErrNotAuthorized) { + data.NotAuthorized = true + return types.ResponseStatus_Success, nil + } + if errors.Is(err, v2constellation.ErrMinipoolLimitReached) { + data.LimitReached = true + return types.ResponseStatus_Success, nil + } + if errors.Is(err, v2constellation.ErrValidatorRequiresExitMessage) { + data.MissingExitMessage = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + data.Signature = signature + return types.ResponseStatus_Success, nil +} diff --git a/server/api/nodeset/constellation/get-registered-address.go b/server/api/nodeset/constellation/get-registered-address.go new file mode 100644 index 0000000..d46ee2d --- /dev/null +++ b/server/api/nodeset/constellation/get-registered-address.go @@ -0,0 +1,78 @@ +package ns_constellation + +import ( + "errors" + "net/url" + + hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type constellationGetRegisteredAddressContextFactory struct { + handler *ConstellationHandler +} + +func (f *constellationGetRegisteredAddressContextFactory) Create(args url.Values) (*constellationGetRegisteredAddressContext, error) { + c := &constellationGetRegisteredAddressContext{ + handler: f.handler, + } + inputErrs := []error{} + return c, errors.Join(inputErrs...) +} + +func (f *constellationGetRegisteredAddressContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*constellationGetRegisteredAddressContext, api.NodeSetConstellation_GetRegisteredAddressData]( + router, "get-registered-address", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type constellationGetRegisteredAddressContext struct { + handler *ConstellationHandler +} + +func (c *constellationGetRegisteredAddressContext) PrepareData(data *api.NodeSetConstellation_GetRegisteredAddressData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + ctx := c.handler.ctx + + // Requirements + err := sp.RequireWalletReady() + if err != nil { + return types.ResponseStatus_WalletNotReady, err + } + err = sp.RequireRegisteredWithNodeSet(ctx) + if err != nil { + if errors.Is(err, hdcommon.ErrNotRegisteredWithNodeSet) { + data.NotRegisteredWithNodeSet = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + // Get the registered address + ns := sp.GetNodeSetServiceManager() + address, err := ns.Constellation_GetRegisteredAddress(ctx) + if err != nil { + return types.ResponseStatus_Error, err + } + + if address == nil { + data.NotRegisteredWithConstellation = true + } else { + data.RegisteredAddress = *address + } + return types.ResponseStatus_Success, nil +} diff --git a/server/api/nodeset/constellation/get-registration-signature.go b/server/api/nodeset/constellation/get-registration-signature.go new file mode 100644 index 0000000..434cd8d --- /dev/null +++ b/server/api/nodeset/constellation/get-registration-signature.go @@ -0,0 +1,78 @@ +package ns_constellation + +import ( + "errors" + "net/url" + + hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type constellationGetRegistrationSignatureContextFactory struct { + handler *ConstellationHandler +} + +func (f *constellationGetRegistrationSignatureContextFactory) Create(args url.Values) (*constellationGetRegistrationSignatureContext, error) { + c := &constellationGetRegistrationSignatureContext{ + handler: f.handler, + } + inputErrs := []error{} + return c, errors.Join(inputErrs...) +} + +func (f *constellationGetRegistrationSignatureContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*constellationGetRegistrationSignatureContext, api.NodeSetConstellation_GetRegistrationSignatureData]( + router, "get-registration-signature", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== +type constellationGetRegistrationSignatureContext struct { + handler *ConstellationHandler +} + +func (c *constellationGetRegistrationSignatureContext) PrepareData(data *api.NodeSetConstellation_GetRegistrationSignatureData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + ctx := c.handler.ctx + + // Requirements + err := sp.RequireWalletReady() + if err != nil { + return types.ResponseStatus_WalletNotReady, err + } + err = sp.RequireRegisteredWithNodeSet(ctx) + if err != nil { + if errors.Is(err, hdcommon.ErrNotRegisteredWithNodeSet) { + data.NotRegistered = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + // Get the registration signature + ns := sp.GetNodeSetServiceManager() + signature, err := ns.Constellation_GetRegistrationSignature(ctx) + if err != nil { + if errors.Is(err, v2constellation.ErrNotAuthorized) { + data.NotAuthorized = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + data.Signature = signature + return types.ResponseStatus_Success, nil +} diff --git a/server/api/nodeset/constellation/get-validators.go b/server/api/nodeset/constellation/get-validators.go new file mode 100644 index 0000000..bdd1f18 --- /dev/null +++ b/server/api/nodeset/constellation/get-validators.go @@ -0,0 +1,79 @@ +package ns_constellation + +import ( + "errors" + "net/url" + + hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type constellationGetValidatorsContextFactory struct { + handler *ConstellationHandler +} + +func (f *constellationGetValidatorsContextFactory) Create(args url.Values) (*constellationGetValidatorsContext, error) { + c := &constellationGetValidatorsContext{ + handler: f.handler, + } + inputErrs := []error{} + return c, errors.Join(inputErrs...) +} + +func (f *constellationGetValidatorsContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*constellationGetValidatorsContext, api.NodeSetConstellation_GetValidatorsData]( + router, "get-validators", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type constellationGetValidatorsContext struct { + handler *ConstellationHandler +} + +func (c *constellationGetValidatorsContext) PrepareData(data *api.NodeSetConstellation_GetValidatorsData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + ctx := c.handler.ctx + + // Requirements + err := sp.RequireWalletReady() + if err != nil { + return types.ResponseStatus_WalletNotReady, err + } + err = sp.RequireRegisteredWithNodeSet(ctx) + if err != nil { + if errors.Is(err, hdcommon.ErrNotRegisteredWithNodeSet) { + data.NotRegistered = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + // Get the registered validators + ns := sp.GetNodeSetServiceManager() + validators, err := ns.Constellation_GetValidators(ctx) + if err != nil { + if errors.Is(err, v2constellation.ErrNotAuthorized) { + data.NotAuthorized = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + data.Validators = validators + return types.ResponseStatus_Success, nil +} diff --git a/server/api/nodeset/constellation/handler.go b/server/api/nodeset/constellation/handler.go new file mode 100644 index 0000000..01a9a32 --- /dev/null +++ b/server/api/nodeset/constellation/handler.go @@ -0,0 +1,40 @@ +package ns_constellation + +import ( + "context" + + "github.com/gorilla/mux" + "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/log" +) + +type ConstellationHandler struct { + logger *log.Logger + ctx context.Context + serviceProvider common.IHyperdriveServiceProvider + factories []server.IContextFactory +} + +func NewConstellationHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *ConstellationHandler { + h := &ConstellationHandler{ + logger: logger, + ctx: ctx, + serviceProvider: serviceProvider, + } + h.factories = []server.IContextFactory{ + &constellationGetDepositSignatureContextFactory{h}, + &constellationGetRegisteredAddressContextFactory{h}, + &constellationGetRegistrationSignatureContextFactory{h}, + &constellationGetValidatorsContextFactory{h}, + &constellationUploadSignedExitsContextFactory{h}, + } + return h +} + +func (h *ConstellationHandler) RegisterRoutes(router *mux.Router) { + subrouter := router.PathPrefix("/constellation").Subrouter() + for _, factory := range h.factories { + factory.RegisterRoute(subrouter) + } +} diff --git a/server/api/nodeset/constellation/upload-signed-exits.go b/server/api/nodeset/constellation/upload-signed-exits.go new file mode 100644 index 0000000..7e51784 --- /dev/null +++ b/server/api/nodeset/constellation/upload-signed-exits.go @@ -0,0 +1,76 @@ +package ns_constellation + +import ( + "errors" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type constellationUploadSignedExitsContextFactory struct { + handler *ConstellationHandler +} + +func (f *constellationUploadSignedExitsContextFactory) Create(body api.NodeSetConstellation_UploadSignedExitsRequestBody) (*constellationUploadSignedExitsContext, error) { + c := &constellationUploadSignedExitsContext{ + handler: f.handler, + body: body, + } + return c, nil +} + +func (f *constellationUploadSignedExitsContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessPost[*constellationUploadSignedExitsContext, api.NodeSetConstellation_UploadSignedExitsRequestBody, api.NodeSetConstellation_UploadSignedExitsData]( + router, "upload-signed-exits", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type constellationUploadSignedExitsContext struct { + handler *ConstellationHandler + body api.NodeSetConstellation_UploadSignedExitsRequestBody +} + +func (c *constellationUploadSignedExitsContext) PrepareData(data *api.NodeSetConstellation_UploadSignedExitsData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + ctx := c.handler.ctx + + // Requirements + err := sp.RequireWalletReady() + if err != nil { + return types.ResponseStatus_WalletNotReady, err + } + err = sp.RequireRegisteredWithNodeSet(ctx) + if err != nil { + if errors.Is(err, hdcommon.ErrNotRegisteredWithNodeSet) { + data.NotRegistered = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + + // Upload the deposit data + ns := sp.GetNodeSetServiceManager() + err = ns.Constellation_UploadSignedExitMessages(ctx, c.body.ExitMessages) + if err != nil { + if errors.Is(err, v2constellation.ErrNotAuthorized) { + data.NotAuthorized = true + return types.ResponseStatus_Success, nil + } + return types.ResponseStatus_Error, err + } + return types.ResponseStatus_Success, nil +} diff --git a/server/api/nodeset/get-registration-status.go b/server/api/nodeset/get-registration-status.go index 9f99073..f188b1d 100644 --- a/server/api/nodeset/get-registration-status.go +++ b/server/api/nodeset/get-registration-status.go @@ -28,7 +28,7 @@ func (f *nodeSetGetRegistrationStatusContextFactory) Create(args url.Values) (*n func (f *nodeSetGetRegistrationStatusContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*nodeSetGetRegistrationStatusContext, api.NodeSetGetRegistrationStatusData]( - router, "get-registration-status", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "get-registration-status", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/nodeset/handler.go b/server/api/nodeset/handler.go index a7d0f03..5d2303b 100644 --- a/server/api/nodeset/handler.go +++ b/server/api/nodeset/handler.go @@ -5,19 +5,22 @@ import ( "github.com/gorilla/mux" "github.com/nodeset-org/hyperdrive-daemon/common" + ns_constellation "github.com/nodeset-org/hyperdrive-daemon/server/api/nodeset/constellation" ns_stakewise "github.com/nodeset-org/hyperdrive-daemon/server/api/nodeset/stakewise" "github.com/rocket-pool/node-manager-core/api/server" "github.com/rocket-pool/node-manager-core/log" ) type NodeSetHandler struct { - logger *log.Logger - ctx context.Context - serviceProvider *common.ServiceProvider - factories []server.IContextFactory + logger *log.Logger + ctx context.Context + serviceProvider common.IHyperdriveServiceProvider + factories []server.IContextFactory + stakeWiseHandler *ns_stakewise.StakeWiseHandler + constellationHandler *ns_constellation.ConstellationHandler } -func NewNodeSetHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *NodeSetHandler { +func NewNodeSetHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *NodeSetHandler { h := &NodeSetHandler{ logger: logger, ctx: ctx, @@ -37,6 +40,10 @@ func (h *NodeSetHandler) RegisterRoutes(router *mux.Router) { } // Register StakeWise routes - stakeWiseHandler := ns_stakewise.NewStakeWiseHandler(h.logger, h.ctx, h.serviceProvider) - stakeWiseHandler.RegisterRoutes(subrouter) + h.stakeWiseHandler = ns_stakewise.NewStakeWiseHandler(h.logger, h.ctx, h.serviceProvider) + h.stakeWiseHandler.RegisterRoutes(subrouter) + + // Register Constellation routes + h.constellationHandler = ns_constellation.NewConstellationHandler(h.logger, h.ctx, h.serviceProvider) + h.constellationHandler.RegisterRoutes(subrouter) } diff --git a/server/api/nodeset/register-node.go b/server/api/nodeset/register-node.go index fb26a13..7ae73ac 100644 --- a/server/api/nodeset/register-node.go +++ b/server/api/nodeset/register-node.go @@ -32,7 +32,7 @@ func (f *nodeSetRegisterNodeContextFactory) Create(args url.Values) (*nodeSetReg func (f *nodeSetRegisterNodeContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*nodeSetRegisterNodeContext, api.NodeSetRegisterNodeData]( - router, "register-node", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "register-node", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -66,6 +66,8 @@ func (c *nodeSetRegisterNodeContext) PrepareData(data *api.NodeSetRegisterNodeDa switch result { case common.RegistrationResult_Success: data.Success = true + // Force a re-login to update the registration status + _ = sp.GetNodeSetServiceManager().Login(ctx) case common.RegistrationResult_AlreadyRegistered: data.Success = false data.AlreadyRegistered = true diff --git a/server/api/nodeset/stakewise/get-registered-validators.go b/server/api/nodeset/stakewise/get-registered-validators.go index 4eca44f..612f550 100644 --- a/server/api/nodeset/stakewise/get-registered-validators.go +++ b/server/api/nodeset/stakewise/get-registered-validators.go @@ -36,7 +36,7 @@ func (f *stakeWiseGetRegisteredValidatorsContextFactory) Create(args url.Values) func (f *stakeWiseGetRegisteredValidatorsContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*stakeWiseGetRegisteredValidatorsContext, api.NodeSetStakeWise_GetRegisteredValidatorsData]( - router, "get-registered-validators", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "get-registered-validators", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/nodeset/stakewise/get-server-deposit-data-version.go b/server/api/nodeset/stakewise/get-server-deposit-data-version.go index 819e2b7..722635d 100644 --- a/server/api/nodeset/stakewise/get-server-deposit-data-version.go +++ b/server/api/nodeset/stakewise/get-server-deposit-data-version.go @@ -36,7 +36,7 @@ func (f *stakeWiseGetDepositDataSetVersionContextFactory) Create(args url.Values func (f *stakeWiseGetDepositDataSetVersionContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*stakeWiseGetDepositDataSetVersionContext, api.NodeSetStakeWise_GetDepositDataSetVersionData]( - router, "get-deposit-data-set/version", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "get-deposit-data-set/version", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/nodeset/stakewise/get-server-deposit-data.go b/server/api/nodeset/stakewise/get-server-deposit-data.go index d7437f2..ed19636 100644 --- a/server/api/nodeset/stakewise/get-server-deposit-data.go +++ b/server/api/nodeset/stakewise/get-server-deposit-data.go @@ -36,7 +36,7 @@ func (f *stakeWiseGetDepositDataSetContextFactory) Create(args url.Values) (*sta func (f *stakeWiseGetDepositDataSetContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*stakeWiseGetDepositDataSetContext, api.NodeSetStakeWise_GetDepositDataSetData]( - router, "get-deposit-data-set", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "get-deposit-data-set", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/nodeset/stakewise/handler.go b/server/api/nodeset/stakewise/handler.go index b85e5a0..f9df3b2 100644 --- a/server/api/nodeset/stakewise/handler.go +++ b/server/api/nodeset/stakewise/handler.go @@ -12,11 +12,11 @@ import ( type StakeWiseHandler struct { logger *log.Logger ctx context.Context - serviceProvider *common.ServiceProvider + serviceProvider common.IHyperdriveServiceProvider factories []server.IContextFactory } -func NewStakeWiseHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *StakeWiseHandler { +func NewStakeWiseHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *StakeWiseHandler { h := &StakeWiseHandler{ logger: logger, ctx: ctx, diff --git a/server/api/nodeset/stakewise/upload-deposit-data.go b/server/api/nodeset/stakewise/upload-deposit-data.go index af35da8..624aa60 100644 --- a/server/api/nodeset/stakewise/upload-deposit-data.go +++ b/server/api/nodeset/stakewise/upload-deposit-data.go @@ -7,8 +7,8 @@ import ( "github.com/gorilla/mux" hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" - apiv1 "github.com/nodeset-org/nodeset-client-go/api-v1" - "github.com/rocket-pool/node-manager-core/beacon" + apiv0 "github.com/nodeset-org/nodeset-client-go/api-v0" + "github.com/nodeset-org/nodeset-client-go/common/stakewise" "github.com/rocket-pool/node-manager-core/api/server" "github.com/rocket-pool/node-manager-core/api/types" @@ -22,7 +22,7 @@ type stakeWiseUploadDepositDataContextFactory struct { handler *StakeWiseHandler } -func (f *stakeWiseUploadDepositDataContextFactory) Create(body []beacon.ExtendedDepositData) (*stakeWiseUploadDepositDataContext, error) { +func (f *stakeWiseUploadDepositDataContextFactory) Create(body api.NodeSetStakeWise_UploadDepositDataRequestBody) (*stakeWiseUploadDepositDataContext, error) { c := &stakeWiseUploadDepositDataContext{ handler: f.handler, body: body, @@ -31,8 +31,8 @@ func (f *stakeWiseUploadDepositDataContextFactory) Create(body []beacon.Extended } func (f *stakeWiseUploadDepositDataContextFactory) RegisterRoute(router *mux.Router) { - server.RegisterQuerylessPost[*stakeWiseUploadDepositDataContext, []beacon.ExtendedDepositData, api.NodeSetStakeWise_UploadDepositDataData]( - router, "upload-deposit-data", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + server.RegisterQuerylessPost[*stakeWiseUploadDepositDataContext, api.NodeSetStakeWise_UploadDepositDataRequestBody, api.NodeSetStakeWise_UploadDepositDataData]( + router, "upload-deposit-data", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -41,7 +41,7 @@ func (f *stakeWiseUploadDepositDataContextFactory) RegisterRoute(router *mux.Rou // =============== type stakeWiseUploadDepositDataContext struct { handler *StakeWiseHandler - body []beacon.ExtendedDepositData + body api.NodeSetStakeWise_UploadDepositDataRequestBody } func (c *stakeWiseUploadDepositDataContext) PrepareData(data *api.NodeSetStakeWise_UploadDepositDataData, opts *bind.TransactOpts) (types.ResponseStatus, error) { @@ -64,13 +64,13 @@ func (c *stakeWiseUploadDepositDataContext) PrepareData(data *api.NodeSetStakeWi // Upload the deposit data ns := sp.GetNodeSetServiceManager() - err = ns.StakeWise_UploadDepositData(ctx, c.body) + err = ns.StakeWise_UploadDepositData(ctx, c.body.Vault, c.body.DepositData) if err != nil { - if errors.Is(err, apiv1.ErrVaultNotFound) { + if errors.Is(err, apiv0.ErrVaultNotFound) { data.VaultNotFound = true return types.ResponseStatus_Success, nil } - if errors.Is(err, apiv1.ErrInvalidPermissions) { + if errors.Is(err, stakewise.ErrInvalidPermissions) { data.InvalidPermissions = true return types.ResponseStatus_Success, nil } diff --git a/server/api/nodeset/stakewise/upload-signed-exits.go b/server/api/nodeset/stakewise/upload-signed-exits.go index 7a1171c..10b575d 100644 --- a/server/api/nodeset/stakewise/upload-signed-exits.go +++ b/server/api/nodeset/stakewise/upload-signed-exits.go @@ -7,7 +7,6 @@ import ( "github.com/gorilla/mux" hdcommon "github.com/nodeset-org/hyperdrive-daemon/common" "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" - apiv1 "github.com/nodeset-org/nodeset-client-go/api-v1" "github.com/rocket-pool/node-manager-core/api/server" "github.com/rocket-pool/node-manager-core/api/types" @@ -21,7 +20,7 @@ type stakeWiseUploadSignedExitsContextFactory struct { handler *StakeWiseHandler } -func (f *stakeWiseUploadSignedExitsContextFactory) Create(body []apiv1.ExitData) (*stakeWiseUploadSignedExitsContext, error) { +func (f *stakeWiseUploadSignedExitsContextFactory) Create(body api.NodeSetStakeWise_UploadSignedExitsRequestBody) (*stakeWiseUploadSignedExitsContext, error) { c := &stakeWiseUploadSignedExitsContext{ handler: f.handler, body: body, @@ -30,8 +29,8 @@ func (f *stakeWiseUploadSignedExitsContextFactory) Create(body []apiv1.ExitData) } func (f *stakeWiseUploadSignedExitsContextFactory) RegisterRoute(router *mux.Router) { - server.RegisterQuerylessPost[*stakeWiseUploadSignedExitsContext, []apiv1.ExitData, api.NodeSetStakeWise_UploadSignedExitsData]( - router, "upload-signed-exits", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + server.RegisterQuerylessPost[*stakeWiseUploadSignedExitsContext, api.NodeSetStakeWise_UploadSignedExitsRequestBody, api.NodeSetStakeWise_UploadSignedExitsData]( + router, "upload-signed-exits", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -40,7 +39,7 @@ func (f *stakeWiseUploadSignedExitsContextFactory) RegisterRoute(router *mux.Rou // =============== type stakeWiseUploadSignedExitsContext struct { handler *StakeWiseHandler - body []apiv1.ExitData + body api.NodeSetStakeWise_UploadSignedExitsRequestBody } func (c *stakeWiseUploadSignedExitsContext) PrepareData(data *api.NodeSetStakeWise_UploadSignedExitsData, opts *bind.TransactOpts) (types.ResponseStatus, error) { @@ -63,7 +62,7 @@ func (c *stakeWiseUploadSignedExitsContext) PrepareData(data *api.NodeSetStakeWi // Upload the deposit data ns := sp.GetNodeSetServiceManager() - err = ns.StakeWise_UploadSignedExitMessages(ctx, c.body) + err = ns.StakeWise_UploadSignedExitMessages(ctx, c.body.Vault, c.body.ExitData) if err != nil { return types.ResponseStatus_Error, err } diff --git a/server/api/service/client-status.go b/server/api/service/client-status.go index 6d57b8c..f51857f 100644 --- a/server/api/service/client-status.go +++ b/server/api/service/client-status.go @@ -28,7 +28,7 @@ func (f *serviceClientStatusContextFactory) Create(args url.Values) (*serviceCli func (f *serviceClientStatusContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*serviceClientStatusContext, api.ServiceClientStatusData]( - router, "client-status", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "client-status", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/service/get-config.go b/server/api/service/get-config.go index 0098b5a..14fcd47 100644 --- a/server/api/service/get-config.go +++ b/server/api/service/get-config.go @@ -27,7 +27,7 @@ func (f *serviceGetConfigContextFactory) Create(args url.Values) (*serviceGetCon func (f *serviceGetConfigContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*serviceGetConfigContext, api.ServiceGetConfigData]( - router, "get-config", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "get-config", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/service/get-network-settings.go b/server/api/service/get-network-settings.go new file mode 100644 index 0000000..e2963e3 --- /dev/null +++ b/server/api/service/get-network-settings.go @@ -0,0 +1,55 @@ +package service + +import ( + "fmt" + "net/url" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type serviceGetNetworkSettingsContextFactory struct { + handler *ServiceHandler +} + +func (f *serviceGetNetworkSettingsContextFactory) Create(args url.Values) (*serviceGetNetworkSettingsContext, error) { + c := &serviceGetNetworkSettingsContext{ + handler: f.handler, + } + return c, nil +} + +func (f *serviceGetNetworkSettingsContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*serviceGetNetworkSettingsContext, api.ServiceGetNetworkSettingsData]( + router, "get-network-settings", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type serviceGetNetworkSettingsContext struct { + handler *ServiceHandler +} + +func (c *serviceGetNetworkSettingsContext) PrepareData(data *api.ServiceGetNetworkSettingsData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + cfg := sp.GetConfig() + settingsList := cfg.GetNetworkSettings() + network := cfg.Network.Value + for _, settings := range settingsList { + if settings.Key == network { + data.Settings = settings + return types.ResponseStatus_Success, nil + } + } + return types.ResponseStatus_Error, fmt.Errorf("hyperdrive has network [%s] selected but there are no settings for it", network) +} diff --git a/server/api/service/get-resources.go b/server/api/service/get-resources.go new file mode 100644 index 0000000..b320bd7 --- /dev/null +++ b/server/api/service/get-resources.go @@ -0,0 +1,46 @@ +package service + +import ( + "net/url" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + "github.com/nodeset-org/hyperdrive-daemon/shared/types/api" + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" +) + +// =============== +// === Factory === +// =============== + +type serviceGetResourcesContextFactory struct { + handler *ServiceHandler +} + +func (f *serviceGetResourcesContextFactory) Create(args url.Values) (*serviceGetResourcesContext, error) { + c := &serviceGetResourcesContext{ + handler: f.handler, + } + return c, nil +} + +func (f *serviceGetResourcesContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*serviceGetResourcesContext, api.ServiceGetResourcesData]( + router, "get-resources", f, f.handler.logger.Logger, f.handler.serviceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type serviceGetResourcesContext struct { + handler *ServiceHandler +} + +func (c *serviceGetResourcesContext) PrepareData(data *api.ServiceGetResourcesData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + data.Resources = sp.GetResources() + return types.ResponseStatus_Success, nil +} diff --git a/server/api/service/handler.go b/server/api/service/handler.go index 713cbde..2b54c16 100644 --- a/server/api/service/handler.go +++ b/server/api/service/handler.go @@ -12,11 +12,11 @@ import ( type ServiceHandler struct { logger *log.Logger ctx context.Context - serviceProvider *common.ServiceProvider + serviceProvider common.IHyperdriveServiceProvider factories []server.IContextFactory } -func NewServiceHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *ServiceHandler { +func NewServiceHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *ServiceHandler { h := &ServiceHandler{ logger: logger, ctx: ctx, @@ -25,6 +25,8 @@ func NewServiceHandler(logger *log.Logger, ctx context.Context, serviceProvider h.factories = []server.IContextFactory{ &serviceClientStatusContextFactory{h}, &serviceGetConfigContextFactory{h}, + &serviceGetNetworkSettingsContextFactory{h}, + &serviceGetResourcesContextFactory{h}, &serviceRestartContainerContextFactory{h}, &serviceRotateLogsContextFactory{h}, &serviceVersionContextFactory{h}, diff --git a/server/api/service/restart-container.go b/server/api/service/restart-container.go index aebd750..f94d948 100644 --- a/server/api/service/restart-container.go +++ b/server/api/service/restart-container.go @@ -31,7 +31,7 @@ func (f *serviceRestartContainerContextFactory) Create(args url.Values) (*servic func (f *serviceRestartContainerContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*serviceRestartContainerContext, types.SuccessData]( - router, "restart-container", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "restart-container", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/service/rotate-logs.go b/server/api/service/rotate-logs.go index 95ed789..8fcc89a 100644 --- a/server/api/service/rotate-logs.go +++ b/server/api/service/rotate-logs.go @@ -27,7 +27,7 @@ func (f *serviceRotateLogsContextFactory) Create(args url.Values) (*serviceRotat func (f *serviceRotateLogsContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*serviceRotateLogsContext, types.SuccessData]( - router, "rotate", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "rotate", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/service/version.go b/server/api/service/version.go index 664bed4..58a7026 100644 --- a/server/api/service/version.go +++ b/server/api/service/version.go @@ -28,7 +28,7 @@ func (f *serviceVersionContextFactory) Create(args url.Values) (*serviceVersionC func (f *serviceVersionContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*serviceVersionContext, api.ServiceVersionData]( - router, "version", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "version", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/tx/batch-sign-txs.go b/server/api/tx/batch-sign-txs.go index 54b86ac..9596472 100644 --- a/server/api/tx/batch-sign-txs.go +++ b/server/api/tx/batch-sign-txs.go @@ -47,7 +47,7 @@ func (f *txBatchSignTxsContextFactory) Create(body api.BatchSubmitTxsBody) (*txB func (f *txBatchSignTxsContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessPost[*txBatchSignTxsContext, api.BatchSubmitTxsBody, api.TxBatchSignTxData]( - router, "batch-sign-txs", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "batch-sign-txs", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/tx/batch-submit-txs.go b/server/api/tx/batch-submit-txs.go index 5753e6d..649be22 100644 --- a/server/api/tx/batch-submit-txs.go +++ b/server/api/tx/batch-submit-txs.go @@ -46,7 +46,7 @@ func (f *txBatchSubmitTxsContextFactory) Create(body api.BatchSubmitTxsBody) (*t func (f *txBatchSubmitTxsContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessPost[*txBatchSubmitTxsContext, api.BatchSubmitTxsBody, api.BatchTxData]( - router, "batch-submit-txs", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "batch-submit-txs", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/tx/handler.go b/server/api/tx/handler.go index 6e21faf..8237710 100644 --- a/server/api/tx/handler.go +++ b/server/api/tx/handler.go @@ -12,11 +12,11 @@ import ( type TxHandler struct { logger *log.Logger ctx context.Context - serviceProvider *common.ServiceProvider + serviceProvider common.IHyperdriveServiceProvider factories []server.IContextFactory } -func NewTxHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *TxHandler { +func NewTxHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *TxHandler { h := &TxHandler{ logger: logger, ctx: ctx, diff --git a/server/api/tx/sign-tx.go b/server/api/tx/sign-tx.go index d3dab27..af51f91 100644 --- a/server/api/tx/sign-tx.go +++ b/server/api/tx/sign-tx.go @@ -43,7 +43,7 @@ func (f *txSignTxContextFactory) Create(body api.SubmitTxBody) (*txSignTxContext func (f *txSignTxContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessPost[*txSignTxContext, api.SubmitTxBody, api.TxSignTxData]( - router, "sign-tx", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "sign-tx", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/tx/submit-tx.go b/server/api/tx/submit-tx.go index 4673c5d..7c6105e 100644 --- a/server/api/tx/submit-tx.go +++ b/server/api/tx/submit-tx.go @@ -42,7 +42,7 @@ func (f *txSubmitTxContextFactory) Create(body api.SubmitTxBody) (*txSubmitTxCon func (f *txSubmitTxContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessPost[*txSubmitTxContext, api.SubmitTxBody, api.TxData]( - router, "submit-tx", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "submit-tx", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/tx/wait.go b/server/api/tx/wait.go index 1d1c01c..98e99eb 100644 --- a/server/api/tx/wait.go +++ b/server/api/tx/wait.go @@ -34,7 +34,7 @@ func (f *txWaitContextFactory) Create(args url.Values) (*txWaitContext, error) { func (f *txWaitContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*txWaitContext, types.SuccessData]( - router, "wait", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "wait", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/utils/handler.go b/server/api/utils/handler.go index 319b2ef..8ea60ef 100644 --- a/server/api/utils/handler.go +++ b/server/api/utils/handler.go @@ -12,11 +12,11 @@ import ( type UtilsHandler struct { logger *log.Logger ctx context.Context - serviceProvider *common.ServiceProvider + serviceProvider common.IHyperdriveServiceProvider factories []server.IContextFactory } -func NewUtilsHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *UtilsHandler { +func NewUtilsHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *UtilsHandler { h := &UtilsHandler{ logger: logger, ctx: ctx, diff --git a/server/api/utils/resolve-ens.go b/server/api/utils/resolve-ens.go index 962236e..d4dba25 100644 --- a/server/api/utils/resolve-ens.go +++ b/server/api/utils/resolve-ens.go @@ -36,7 +36,7 @@ func (f *utilsResolveEnsContextFactory) Create(args url.Values) (*utilsResolveEn func (f *utilsResolveEnsContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*utilsResolveEnsContext, api.UtilsResolveEnsData]( - router, "resolve-ens", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "resolve-ens", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/balance.go b/server/api/wallet/balance.go index a40cd8e..5e5f060 100644 --- a/server/api/wallet/balance.go +++ b/server/api/wallet/balance.go @@ -27,7 +27,7 @@ func (f *walletBalanceContextFactory) Create(args url.Values) (*walletBalanceCon func (f *walletBalanceContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletBalanceContext, api.WalletBalanceData]( - router, "balance", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "balance", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/delete-password.go b/server/api/wallet/delete-password.go index f8b2b80..72cf6d0 100644 --- a/server/api/wallet/delete-password.go +++ b/server/api/wallet/delete-password.go @@ -27,7 +27,7 @@ func (f *walletDeletePasswordContextFactory) Create(args url.Values) (*walletDel func (f *walletDeletePasswordContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletDeletePasswordContext, types.SuccessData]( - router, "delete-password", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "delete-password", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/export-eth-key.go b/server/api/wallet/export-eth-key.go index 3deb175..284ff54 100644 --- a/server/api/wallet/export-eth-key.go +++ b/server/api/wallet/export-eth-key.go @@ -29,7 +29,7 @@ func (f *walletExportEthKeyContextFactory) Create(args url.Values) (*walletExpor func (f *walletExportEthKeyContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletExportEthKeyContext, api.WalletExportEthKeyData]( - router, "export-eth-key", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "export-eth-key", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/export.go b/server/api/wallet/export.go index d612c29..325a7f2 100644 --- a/server/api/wallet/export.go +++ b/server/api/wallet/export.go @@ -28,7 +28,7 @@ func (f *walletExportContextFactory) Create(args url.Values) (*walletExportConte func (f *walletExportContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletExportContext, api.WalletExportData]( - router, "export", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "export", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/generate-validator-key.go b/server/api/wallet/generate-validator-key.go index 15d50ca..c54688c 100644 --- a/server/api/wallet/generate-validator-key.go +++ b/server/api/wallet/generate-validator-key.go @@ -33,7 +33,7 @@ func (f *walletGenerateValidatorKeyContextFactory) Create(args url.Values) (*wal func (f *walletGenerateValidatorKeyContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletGenerateValidatorKeyContext, api.WalletGenerateValidatorKeyData]( - router, "generate-validator-key", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "generate-validator-key", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/handler.go b/server/api/wallet/handler.go index 0ae8174..7df2172 100644 --- a/server/api/wallet/handler.go +++ b/server/api/wallet/handler.go @@ -12,11 +12,11 @@ import ( type WalletHandler struct { logger *log.Logger ctx context.Context - serviceProvider *common.ServiceProvider + serviceProvider common.IHyperdriveServiceProvider factories []server.IContextFactory } -func NewWalletHandler(logger *log.Logger, ctx context.Context, serviceProvider *common.ServiceProvider) *WalletHandler { +func NewWalletHandler(logger *log.Logger, ctx context.Context, serviceProvider common.IHyperdriveServiceProvider) *WalletHandler { h := &WalletHandler{ logger: logger, ctx: ctx, diff --git a/server/api/wallet/initialize.go b/server/api/wallet/initialize.go index 8f0899f..41056e7 100644 --- a/server/api/wallet/initialize.go +++ b/server/api/wallet/initialize.go @@ -39,7 +39,7 @@ func (f *walletInitializeContextFactory) Create(args url.Values) (*walletInitial func (f *walletInitializeContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletInitializeContext, api.WalletInitializeData]( - router, "initialize", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "initialize", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/masquerade.go b/server/api/wallet/masquerade.go index 84dfae2..a35d4ce 100644 --- a/server/api/wallet/masquerade.go +++ b/server/api/wallet/masquerade.go @@ -33,7 +33,7 @@ func (f *walletMasqueradeContextFactory) Create(args url.Values) (*walletMasquer func (f *walletMasqueradeContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletMasqueradeContext, types.SuccessData]( - router, "masquerade", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "masquerade", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/recover.go b/server/api/wallet/recover.go index 8a29f1a..7850b53 100644 --- a/server/api/wallet/recover.go +++ b/server/api/wallet/recover.go @@ -38,7 +38,7 @@ func (f *walletRecoverContextFactory) Create(args url.Values) (*walletRecoverCon func (f *walletRecoverContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletRecoverContext, api.WalletRecoverData]( - router, "recover", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "recover", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/restore-address.go b/server/api/wallet/restore-address.go index 133a778..e3a6042 100644 --- a/server/api/wallet/restore-address.go +++ b/server/api/wallet/restore-address.go @@ -27,7 +27,7 @@ func (f *walletRestoreAddressContextFactory) Create(args url.Values) (*walletRes func (f *walletRestoreAddressContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletRestoreAddressContext, types.SuccessData]( - router, "restore-address", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "restore-address", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/search-and-recover.go b/server/api/wallet/search-and-recover.go index b5fe73e..828779c 100644 --- a/server/api/wallet/search-and-recover.go +++ b/server/api/wallet/search-and-recover.go @@ -43,7 +43,7 @@ func (f *walletSearchAndRecoverContextFactory) Create(args url.Values) (*walletS func (f *walletSearchAndRecoverContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSearchAndRecoverContext, api.WalletSearchAndRecoverData]( - router, "search-and-recover", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "search-and-recover", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -62,7 +62,7 @@ type walletSearchAndRecoverContext struct { func (c *walletSearchAndRecoverContext) PrepareData(data *api.WalletSearchAndRecoverData, opts *bind.TransactOpts) (types.ResponseStatus, error) { sp := c.handler.serviceProvider w := sp.GetWallet() - rs := sp.GetConfig().GetNetworkResources() + rs := sp.GetResources() // Requirements status, err := w.GetStatus() diff --git a/server/api/wallet/send-message.go b/server/api/wallet/send-message.go index 179770a..1998d5c 100644 --- a/server/api/wallet/send-message.go +++ b/server/api/wallet/send-message.go @@ -34,7 +34,7 @@ func (f *walletSendMessageContextFactory) Create(args url.Values) (*walletSendMe func (f *walletSendMessageContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSendMessageContext, types.TxInfoData]( - router, "send-message", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "send-message", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/send.go b/server/api/wallet/send.go index 462311b..f2b7b04 100644 --- a/server/api/wallet/send.go +++ b/server/api/wallet/send.go @@ -43,7 +43,7 @@ func (f *walletSendContextFactory) Create(args url.Values) (*walletSendContext, func (f *walletSendContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSendContext, api.WalletSendData]( - router, "send", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "send", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -89,7 +89,7 @@ func (c *walletSendContext) PrepareData(data *api.WalletSendData, opts *bind.Tra tokenAddress := common.HexToAddress(c.token) // Make a binding for it - tokenContract, err := contracts.NewErc20Contract(tokenAddress, ec, qMgr, txMgr, nil) + tokenContract, err = contracts.NewErc20Contract(tokenAddress, ec, qMgr, txMgr, nil) if err != nil { return types.ResponseStatus_Error, fmt.Errorf("error creating ERC20 contract binding: %w", err) } diff --git a/server/api/wallet/set-ens-name.go b/server/api/wallet/set-ens-name.go index bcc8a9c..dacd033 100644 --- a/server/api/wallet/set-ens-name.go +++ b/server/api/wallet/set-ens-name.go @@ -33,7 +33,7 @@ func (f *walletSetEnsNameContextFactory) Create(args url.Values) (*walletSetEnsN func (f *walletSetEnsNameContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSetEnsNameContext, api.WalletSetEnsNameData]( - router, "set-ens-name", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "set-ens-name", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/set-password.go b/server/api/wallet/set-password.go index b6a8b83..48b9de4 100644 --- a/server/api/wallet/set-password.go +++ b/server/api/wallet/set-password.go @@ -32,7 +32,7 @@ func (f *walletSetPasswordContextFactory) Create(args url.Values) (*walletSetPas func (f *walletSetPasswordContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSetPasswordContext, types.SuccessData]( - router, "set-password", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "set-password", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/sign-message.go b/server/api/wallet/sign-message.go index dfcb2e0..88cb1a9 100644 --- a/server/api/wallet/sign-message.go +++ b/server/api/wallet/sign-message.go @@ -34,7 +34,7 @@ func (f *walletSignMessageContextFactory) Create(args url.Values) (*walletSignMe func (f *walletSignMessageContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSignMessageContext, api.WalletSignMessageData]( - router, "sign-message", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "sign-message", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/sign-tx.go b/server/api/wallet/sign-tx.go index d251611..24b0255 100644 --- a/server/api/wallet/sign-tx.go +++ b/server/api/wallet/sign-tx.go @@ -34,7 +34,7 @@ func (f *walletSignTxContextFactory) Create(args url.Values) (*walletSignTxConte func (f *walletSignTxContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletSignTxContext, api.WalletSignTxData]( - router, "sign-tx", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "sign-tx", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/status.go b/server/api/wallet/status.go index f9652b0..7459d8d 100644 --- a/server/api/wallet/status.go +++ b/server/api/wallet/status.go @@ -27,7 +27,7 @@ func (f *walletStatusContextFactory) Create(args url.Values) (*walletStatusConte func (f *walletStatusContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletStatusContext, api.WalletStatusData]( - router, "status", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "status", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } diff --git a/server/api/wallet/test-recover.go b/server/api/wallet/test-recover.go index 3ce3d06..714daa6 100644 --- a/server/api/wallet/test-recover.go +++ b/server/api/wallet/test-recover.go @@ -37,7 +37,7 @@ func (f *walletTestRecoverContextFactory) Create(args url.Values) (*walletTestRe func (f *walletTestRecoverContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletTestRecoverContext, api.WalletRecoverData]( - router, "test-recover", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "test-recover", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -54,7 +54,7 @@ type walletTestRecoverContext struct { func (c *walletTestRecoverContext) PrepareData(data *api.WalletRecoverData, opts *bind.TransactOpts) (types.ResponseStatus, error) { sp := c.handler.serviceProvider - rs := sp.GetConfig().GetNetworkResources() + rs := sp.GetResources() // Parse the derivation path path, err := wallet.GetDerivationPath(wallet.DerivationPath(c.derivationPath)) diff --git a/server/api/wallet/test-search-and-recover.go b/server/api/wallet/test-search-and-recover.go index 54dfbaa..f171b8f 100644 --- a/server/api/wallet/test-search-and-recover.go +++ b/server/api/wallet/test-search-and-recover.go @@ -37,7 +37,7 @@ func (f *walletTestSearchAndRecoverContextFactory) Create(args url.Values) (*wal func (f *walletTestSearchAndRecoverContextFactory) RegisterRoute(router *mux.Router) { server.RegisterQuerylessGet[*walletTestSearchAndRecoverContext, api.WalletSearchAndRecoverData]( - router, "test-search-and-recover", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + router, "test-search-and-recover", f, f.handler.logger.Logger, f.handler.serviceProvider, ) } @@ -53,7 +53,7 @@ type walletTestSearchAndRecoverContext struct { func (c *walletTestSearchAndRecoverContext) PrepareData(data *api.WalletSearchAndRecoverData, opts *bind.TransactOpts) (types.ResponseStatus, error) { sp := c.handler.serviceProvider - rs := sp.GetConfig().GetNetworkResources() + rs := sp.GetResources() // Try each derivation path across all of the iterations var recoveredWallet *nodewallet.Wallet diff --git a/server/server-manager.go b/server/server-manager.go index 0527c48..e0ecd18 100644 --- a/server/server-manager.go +++ b/server/server-manager.go @@ -21,7 +21,7 @@ type ServerManager struct { } // Creates a new server manager -func NewServerManager(sp *common.ServiceProvider, ip string, port uint16, stopWg *sync.WaitGroup) (*ServerManager, error) { +func NewServerManager(sp common.IHyperdriveServiceProvider, ip string, port uint16, stopWg *sync.WaitGroup) (*ServerManager, error) { // Start the API server apiServer, err := createServer(sp, ip, port) if err != nil { @@ -55,7 +55,7 @@ func (m *ServerManager) Stop() { } // Creates a new Hyperdrive API server -func createServer(sp *common.ServiceProvider, ip string, port uint16) (*server.NetworkSocketApiServer, error) { +func createServer(sp common.IHyperdriveServiceProvider, ip string, port uint16) (*server.NetworkSocketApiServer, error) { apiLogger := sp.GetApiLogger() ctx := apiLogger.CreateContextWithLogger(sp.GetBaseContext()) diff --git a/shared/config/enums.go b/shared/config/enums.go index 6a09b55..57f7ae1 100644 --- a/shared/config/enums.go +++ b/shared/config/enums.go @@ -9,7 +9,7 @@ const ( Network_HoleskyDev config.Network = "holesky-dev" // Local test network for development - Network_LocalTest config.Network = "hd-local-test" + Network_LocalTest config.Network = "local-test" ) type MevRelayID string @@ -20,7 +20,6 @@ const ( MevRelayID_Flashbots MevRelayID = "flashbots" MevRelayID_BloxrouteMaxProfit MevRelayID = "bloxrouteMaxProfit" MevRelayID_BloxrouteRegulated MevRelayID = "bloxrouteRegulated" - MevRelayID_Eden MevRelayID = "eden" MevRelayID_TitanRegional MevRelayID = "titanRegional" ) diff --git a/shared/config/hyperdrive-config.go b/shared/config/hyperdrive-config.go index 1d31d8d..288374b 100644 --- a/shared/config/hyperdrive-config.go +++ b/shared/config/hyperdrive-config.go @@ -5,7 +5,7 @@ import ( "os" "path/filepath" "reflect" - "strings" + "sort" "github.com/alessio/shellescape" "github.com/nodeset-org/hyperdrive-daemon/shared" @@ -68,11 +68,12 @@ type HyperdriveConfig struct { // Internal fields Version string hyperdriveUserDirectory string - resources *HyperdriveResources + ethNetworkName string + networkSettings []*HyperdriveSettings } // Load configuration settings from a file -func LoadFromFile(path string) (*HyperdriveConfig, error) { +func LoadFromFile(path string, networks []*HyperdriveSettings) (*HyperdriveConfig, error) { // Return nil if the file doesn't exist _, err := os.Stat(path) if os.IsNotExist(err) { @@ -92,7 +93,10 @@ func LoadFromFile(path string) (*HyperdriveConfig, error) { } // Deserialize it into a config object - cfg := NewHyperdriveConfig(filepath.Dir(path)) + cfg, err := NewHyperdriveConfig(filepath.Dir(path), networks) + if err != nil { + return nil, fmt.Errorf("could not create Hyperdrive config: %w", err) + } err = cfg.Deserialize(settings) if err != nil { return nil, fmt.Errorf("could not deserialize settings file: %w", err) @@ -102,23 +106,15 @@ func LoadFromFile(path string) (*HyperdriveConfig, error) { } // Creates a new Hyperdrive configuration instance -func NewHyperdriveConfig(hdDir string) *HyperdriveConfig { - cfg := newHyperdriveConfigImpl(hdDir, config.Network_Mainnet) // Default to mainnet - cfg.updateResources() - return cfg -} - -// Creates a new Hyperdrive configuration instance for a custom network -func NewHyperdriveConfigForNetwork(hdDir string, network config.Network, resources *HyperdriveResources) *HyperdriveConfig { - cfg := newHyperdriveConfigImpl(hdDir, network) - cfg.resources = resources - return cfg +func NewHyperdriveConfig(hdDir string, networks []*HyperdriveSettings) (*HyperdriveConfig, error) { + return NewHyperdriveConfigForNetwork(hdDir, networks, config.Network_Mainnet) // Default to mainnet } -// Implementation of the Hyperdrive config constructor -func newHyperdriveConfigImpl(hdDir string, network config.Network) *HyperdriveConfig { +// Creates a new Hyperdrive configuration instance for a specific network +func NewHyperdriveConfigForNetwork(hdDir string, networks []*HyperdriveSettings, selectedNetwork config.Network) (*HyperdriveConfig, error) { cfg := &HyperdriveConfig{ hyperdriveUserDirectory: hdDir, + networkSettings: networks, Modules: map[string]any{}, ProjectName: config.Parameter[string]{ @@ -154,13 +150,13 @@ func newHyperdriveConfigImpl(hdDir string, network config.Network) *HyperdriveCo ID: ids.NetworkID, Name: "Network", Description: "The Ethereum network you want to use - select Holesky Testnet to practice with fake ETH, or Mainnet to stake on the real network using real ETH.", - AffectsContainers: []config.ContainerID{config.ContainerID_Daemon, config.ContainerID_ExecutionClient, config.ContainerID_BeaconNode, config.ContainerID_ValidatorClient}, + AffectsContainers: []config.ContainerID{config.ContainerID_Daemon, config.ContainerID_ExecutionClient, config.ContainerID_BeaconNode, config.ContainerID_ValidatorClient, config.ContainerID_MevBoost}, CanBeBlank: false, OverwriteOnUpgrade: false, }, - Options: getNetworkOptions(), + Options: getNetworkOptions(networks), Default: map[config.Network]config.Network{ - config.Network_All: config.Network_Mainnet, + config.Network_All: selectedNetwork, }, }, @@ -293,19 +289,30 @@ func newHyperdriveConfigImpl(hdDir string, network config.Network) *HyperdriveCo // Create the subconfigs cfg.Logging = config.NewLoggerConfig() - cfg.LocalExecutionClient = NewLocalExecutionClient() + cfg.LocalExecutionClient = config.NewLocalExecutionConfig() cfg.ExternalExecutionClient = config.NewExternalExecutionConfig() - cfg.LocalBeaconClient = NewLocalBeaconClient() + cfg.LocalBeaconClient = config.NewLocalBeaconConfig() cfg.ExternalBeaconClient = config.NewExternalBeaconConfig() cfg.Fallback = config.NewFallbackConfig() cfg.Metrics = NewMetricsConfig() cfg.MevBoost = NewMevBoostConfig(cfg) + // Provision the defaults for each network + for _, network := range networks { + err := config.SetDefaultsForNetworks(cfg, network.DefaultConfigSettings, network.Key) + if err != nil { + return nil, fmt.Errorf("could not set defaults for network %s: %w", network.Key, err) + } + if network.Key == selectedNetwork { + cfg.ethNetworkName = network.NetworkResources.EthNetworkName + } + } + // Apply the default values for the network - cfg.Network.Value = network + cfg.Network.Value = selectedNetwork cfg.applyAllDefaults() - return cfg + return cfg, nil } // Get the title for this config @@ -351,6 +358,7 @@ func (cfg *HyperdriveConfig) Serialize(modules []IModuleConfig, includeUserDir b hdMap := config.Serialize(cfg) masterMap[ids.VersionID] = fmt.Sprintf("v%s", shared.HyperdriveVersion) masterMap[ids.RootConfigID] = hdMap + masterMap[ids.EthNetworkNameID] = cfg.ethNetworkName if includeUserDir { masterMap[ids.UserDirID] = cfg.hyperdriveUserDirectory @@ -414,6 +422,10 @@ func (cfg *HyperdriveConfig) Deserialize(masterMap map[string]any) error { if exists { cfg.hyperdriveUserDirectory = userDir.(string) } + ethNetworkName, exists := masterMap[ids.EthNetworkNameID] + if exists { + cfg.ethNetworkName = ethNetworkName.(string) + } // Handle modules modules, exists := masterMap[ModulesName] @@ -427,7 +439,6 @@ func (cfg *HyperdriveConfig) Deserialize(masterMap map[string]any) error { cfg.Modules = map[string]any{} } - cfg.updateResources() return nil } @@ -440,17 +451,24 @@ func (cfg *HyperdriveConfig) ChangeNetwork(newNetwork config.Network) { } cfg.Network.Value = newNetwork + // Change the Eth network name + for _, settings := range cfg.networkSettings { + if settings.Key == newNetwork { + cfg.ethNetworkName = settings.NetworkResources.EthNetworkName + break + } + } + // Run the changes config.ChangeNetwork(cfg, oldNetwork, newNetwork) - cfg.updateResources() } // Creates a copy of the configuration func (cfg *HyperdriveConfig) Clone() *HyperdriveConfig { - clone := NewHyperdriveConfig(cfg.hyperdriveUserDirectory) + clone, _ := NewHyperdriveConfig(cfg.hyperdriveUserDirectory, cfg.networkSettings) config.Clone(cfg, clone, cfg.Network.Value) - clone.updateResources() clone.Version = cfg.Version + clone.ethNetworkName = cfg.ethNetworkName return clone } @@ -465,43 +483,55 @@ func (cfg *HyperdriveConfig) applyAllDefaults() { } // Get the list of options for networks to run on -func getNetworkOptions() []*config.ParameterOption[config.Network] { - options := []*config.ParameterOption[config.Network]{ - { - ParameterOptionCommon: &config.ParameterOptionCommon{ - Name: "Ethereum Mainnet", - Description: "This is the real Ethereum main network, using real ETH to make real validators.", - }, - Value: config.Network_Mainnet, - }, - { - ParameterOptionCommon: &config.ParameterOptionCommon{ - Name: "Holesky Testnet", - Description: "This is the Holešky (Holešovice) test network, which is the next generation of long-lived testnets for Ethereum. It uses free fake ETH to make fake validators.\nUse this if you want to practice running Hyperdrive in a free, safe environment before moving to Mainnet.", - }, - Value: config.Network_Holesky, - }, - } - - if strings.HasSuffix(shared.HyperdriveVersion, "-dev") { +func getNetworkOptions(networks []*HyperdriveSettings) []*config.ParameterOption[config.Network] { + // Create the options + options := []*config.ParameterOption[config.Network]{} + for _, network := range networks { options = append(options, &config.ParameterOption[config.Network]{ ParameterOptionCommon: &config.ParameterOptionCommon{ - Name: "Devnet", - Description: "This is a development network used by Hyperdrive engineers to test new features and contract upgrades before they are promoted to Holesky for staging. You should not use this network unless invited to do so by the developers.", + Name: network.Name, + Description: network.Description, }, - Value: Network_HoleskyDev, + Value: network.Key, }) } + // Sort the options so mainnet comes first and holesky comes second + sort.SliceStable(options, func(i, j int) bool { + firstOption := options[i] + secondOption := options[j] + + // Mainnet comes first + if firstOption.Value == config.Network_Mainnet { + return true + } + if secondOption.Value == config.Network_Mainnet { + return false + } + + // Holesky comes second + if firstOption.Value == config.Network_Holesky { + return true + } + if secondOption.Value == config.Network_Holesky { + return false + } + + // The rest doesn't matter so just sort it alphabetically + return firstOption.Value < secondOption.Value + }) + return options } -func (cfg *HyperdriveConfig) GetResources() *HyperdriveResources { - return cfg.resources +// Get the Eth network name of the selected network +func (cfg *HyperdriveConfig) GetEthNetworkName() string { + return cfg.ethNetworkName } -func (cfg *HyperdriveConfig) updateResources() { - cfg.resources = NewHyperdriveResources(cfg.Network.Value) +// Get all loaded network settings +func (cfg *HyperdriveConfig) GetNetworkSettings() []*HyperdriveSettings { + return cfg.networkSettings } func (cfg *HyperdriveConfig) GetUserDirectory() string { @@ -532,10 +562,6 @@ func (cfg *HyperdriveConfig) GetPasswordFilePath() string { return filepath.Join(cfg.UserDataPath.Value, UserPasswordFilename) } -func (cfg *HyperdriveConfig) GetNetworkResources() *config.NetworkResources { - return cfg.resources.NetworkResources -} - func (cfg *HyperdriveConfig) GetExecutionClientUrls() (string, string) { primaryEcUrl := cfg.GetEcHttpEndpoint() var fallbackEcUrl string diff --git a/shared/config/ids/hyperdrive.go b/shared/config/ids/hyperdrive.go index a65e5d3..a37fed4 100644 --- a/shared/config/ids/hyperdrive.go +++ b/shared/config/ids/hyperdrive.go @@ -5,6 +5,7 @@ const ( RootConfigID string = "hyperdrive" VersionID string = "version" UserDirID string = "hdUserDir" + EthNetworkNameID string = "ethNetworkName" ApiPortID string = "apiPort" NetworkID string = "network" EnableIPv6ID string = "enableIPv6" diff --git a/shared/config/local-beacon-config.go b/shared/config/local-beacon-config.go deleted file mode 100644 index 9a94902..0000000 --- a/shared/config/local-beacon-config.go +++ /dev/null @@ -1,23 +0,0 @@ -package config - -import ( - "github.com/rocket-pool/node-manager-core/config" -) - -// Create a new LocalBeaconClient struct -func NewLocalBeaconClient() *config.LocalBeaconConfig { - cfg := config.NewLocalBeaconConfig() - - cfg.Lighthouse.ContainerTag.Default[Network_HoleskyDev] = cfg.Lighthouse.ContainerTag.Default[config.Network_Holesky] - cfg.Lodestar.ContainerTag.Default[Network_HoleskyDev] = cfg.Lodestar.ContainerTag.Default[config.Network_Holesky] - cfg.Nimbus.ContainerTag.Default[Network_HoleskyDev] = cfg.Nimbus.ContainerTag.Default[config.Network_Holesky] - cfg.Prysm.ContainerTag.Default[Network_HoleskyDev] = cfg.Prysm.ContainerTag.Default[config.Network_Holesky] - cfg.Teku.ContainerTag.Default[Network_HoleskyDev] = cfg.Teku.ContainerTag.Default[config.Network_Holesky] - - cfg.Lighthouse.ContainerTag.Default[Network_LocalTest] = cfg.Lighthouse.ContainerTag.Default[config.Network_Holesky] - cfg.Lodestar.ContainerTag.Default[Network_LocalTest] = cfg.Lodestar.ContainerTag.Default[config.Network_Holesky] - cfg.Nimbus.ContainerTag.Default[Network_LocalTest] = cfg.Nimbus.ContainerTag.Default[config.Network_Holesky] - cfg.Prysm.ContainerTag.Default[Network_LocalTest] = cfg.Prysm.ContainerTag.Default[config.Network_Holesky] - cfg.Teku.ContainerTag.Default[Network_LocalTest] = cfg.Teku.ContainerTag.Default[config.Network_Holesky] - return cfg -} diff --git a/shared/config/local-execution-config.go b/shared/config/local-execution-config.go deleted file mode 100644 index 59929d8..0000000 --- a/shared/config/local-execution-config.go +++ /dev/null @@ -1,23 +0,0 @@ -package config - -import ( - "github.com/rocket-pool/node-manager-core/config" -) - -// Create a new LocalExecutionClient struct -func NewLocalExecutionClient() *config.LocalExecutionConfig { - cfg := config.NewLocalExecutionConfig() - - cfg.Besu.ContainerTag.Default[Network_HoleskyDev] = cfg.Besu.ContainerTag.Default[config.Network_Holesky] - cfg.Geth.ContainerTag.Default[Network_HoleskyDev] = cfg.Geth.ContainerTag.Default[config.Network_Holesky] - cfg.Nethermind.ContainerTag.Default[Network_HoleskyDev] = cfg.Nethermind.ContainerTag.Default[config.Network_Holesky] - cfg.Nethermind.FullPruningThresholdMb.Default[Network_HoleskyDev] = cfg.Nethermind.FullPruningThresholdMb.Default[config.Network_Holesky] - cfg.Reth.ContainerTag.Default[Network_HoleskyDev] = cfg.Reth.ContainerTag.Default[config.Network_Holesky] - - cfg.Besu.ContainerTag.Default[Network_LocalTest] = cfg.Besu.ContainerTag.Default[config.Network_Holesky] - cfg.Geth.ContainerTag.Default[Network_LocalTest] = cfg.Geth.ContainerTag.Default[config.Network_Holesky] - cfg.Nethermind.ContainerTag.Default[Network_LocalTest] = cfg.Nethermind.ContainerTag.Default[config.Network_Holesky] - cfg.Nethermind.FullPruningThresholdMb.Default[Network_LocalTest] = cfg.Nethermind.FullPruningThresholdMb.Default[config.Network_Holesky] - cfg.Reth.ContainerTag.Default[Network_LocalTest] = cfg.Reth.ContainerTag.Default[config.Network_Holesky] - return cfg -} diff --git a/shared/config/mev-boost-config.go b/shared/config/mev-boost-config.go index f07aa66..651530e 100644 --- a/shared/config/mev-boost-config.go +++ b/shared/config/mev-boost-config.go @@ -19,7 +19,7 @@ type MevRelay struct { ID MevRelayID Name string Description string - Urls map[config.Network]string + Urls map[string]string } // Configuration for MEV-Boost @@ -42,9 +42,6 @@ type MevBoostConfig struct { // bloXroute regulated relay BloxRouteRegulatedRelay config.Parameter[bool] - // Eden relay - EdenRelay config.Parameter[bool] - // Titan regional relay TitanRegionalRelay config.Parameter[bool] @@ -161,7 +158,6 @@ func NewMevBoostConfig(parent *HyperdriveConfig) *MevBoostConfig { FlashbotsRelay: generateRelayParameter(ids.MevBoostFlashbotsID, relayMap[MevRelayID_Flashbots]), BloxRouteMaxProfitRelay: generateRelayParameter(ids.MevBoostBloxRouteMaxProfitID, relayMap[MevRelayID_BloxrouteMaxProfit]), BloxRouteRegulatedRelay: generateRelayParameter(ids.MevBoostBloxRouteRegulatedID, relayMap[MevRelayID_BloxrouteRegulated]), - EdenRelay: generateRelayParameter(ids.MevBoostEdenID, relayMap[MevRelayID_Eden]), TitanRegionalRelay: generateRelayParameter(ids.MevBoostTitanRegionalID, relayMap[MevRelayID_TitanRegional]), CustomRelays: config.Parameter[string]{ @@ -236,7 +232,7 @@ func NewMevBoostConfig(parent *HyperdriveConfig) *MevBoostConfig { ParameterCommon: &config.ParameterCommon{ ID: ids.MevBoostExternalUrlID, Name: "External URL", - Description: "The URL of the external MEV-Boost client or provider", + Description: "The URL of the external MEV-Boost client or provider.\nNOTE: If you are running it on the same machine as this node, addresses like `localhost` and `127.0.0.1` will not work due to Docker limitations. Enter your machine's LAN IP address instead, for example 'http://192.168.1.100:18550'.", AffectsContainers: []config.ContainerID{config.ContainerID_BeaconNode}, CanBeBlank: true, OverwriteOnUpgrade: false, @@ -265,7 +261,6 @@ func (cfg *MevBoostConfig) GetParameters() []config.IParameter { &cfg.FlashbotsRelay, &cfg.BloxRouteMaxProfitRelay, &cfg.BloxRouteRegulatedRelay, - &cfg.EdenRelay, &cfg.TitanRegionalRelay, &cfg.CustomRelays, &cfg.Port, @@ -283,9 +278,14 @@ func (cfg *MevBoostConfig) GetSubconfigs() map[string]config.IConfigSection { // Checks if any relays are available for the current network func (cfg *MevBoostConfig) HasRelays() bool { - currentNetwork := cfg.parent.Network.Value + networkName := cfg.parent.GetEthNetworkName() + if networkName == "" { + return false + } + + // Check if any of the relays are available for that Eth network for _, relay := range cfg.relays { - _, exists := relay.Urls[currentNetwork] + _, exists := relay.Urls[networkName] if !exists { continue } @@ -298,27 +298,33 @@ func (cfg *MevBoostConfig) HasRelays() bool { // Get the relays that are available for the current network func (cfg *MevBoostConfig) GetAvailableRelays() []MevRelay { relays := []MevRelay{} - currentNetwork := cfg.parent.Network.Value + networkName := cfg.parent.GetEthNetworkName() + if networkName == "" { + return relays + } + for _, relay := range cfg.relays { - _, exists := relay.Urls[currentNetwork] + _, exists := relay.Urls[networkName] if !exists { continue } relays = append(relays, relay) } - return relays } // Get which MEV-boost relays are enabled func (cfg *MevBoostConfig) GetEnabledMevRelays() []MevRelay { relays := []MevRelay{} + networkName := cfg.parent.GetEthNetworkName() + if networkName == "" { + return relays + } - currentNetwork := cfg.parent.Network.Value switch cfg.SelectionMode.Value { case MevSelectionMode_All: for _, relay := range cfg.relays { - _, exists := relay.Urls[currentNetwork] + _, exists := relay.Urls[networkName] if !exists { // Skip relays that don't exist on the current network continue @@ -328,31 +334,25 @@ func (cfg *MevBoostConfig) GetEnabledMevRelays() []MevRelay { case MevSelectionMode_Manual: if cfg.FlashbotsRelay.Value { - _, exists := cfg.relayMap[MevRelayID_Flashbots].Urls[currentNetwork] + _, exists := cfg.relayMap[MevRelayID_Flashbots].Urls[networkName] if exists { relays = append(relays, cfg.relayMap[MevRelayID_Flashbots]) } } if cfg.BloxRouteMaxProfitRelay.Value { - _, exists := cfg.relayMap[MevRelayID_BloxrouteMaxProfit].Urls[currentNetwork] + _, exists := cfg.relayMap[MevRelayID_BloxrouteMaxProfit].Urls[networkName] if exists { relays = append(relays, cfg.relayMap[MevRelayID_BloxrouteMaxProfit]) } } if cfg.BloxRouteRegulatedRelay.Value { - _, exists := cfg.relayMap[MevRelayID_BloxrouteRegulated].Urls[currentNetwork] + _, exists := cfg.relayMap[MevRelayID_BloxrouteRegulated].Urls[networkName] if exists { relays = append(relays, cfg.relayMap[MevRelayID_BloxrouteRegulated]) } } - if cfg.EdenRelay.Value { - _, exists := cfg.relayMap[MevRelayID_Eden].Urls[currentNetwork] - if exists { - relays = append(relays, cfg.relayMap[MevRelayID_Eden]) - } - } if cfg.TitanRegionalRelay.Value { - _, exists := cfg.relayMap[MevRelayID_TitanRegional].Urls[currentNetwork] + _, exists := cfg.relayMap[MevRelayID_TitanRegional].Urls[networkName] if exists { relays = append(relays, cfg.relayMap[MevRelayID_TitanRegional]) } @@ -364,11 +364,14 @@ func (cfg *MevBoostConfig) GetEnabledMevRelays() []MevRelay { func (cfg *MevBoostConfig) GetRelayString() string { relayUrls := []string{} - currentNetwork := cfg.parent.Network.Value + networkName := cfg.parent.GetEthNetworkName() + if networkName == "" { + return "" + } relays := cfg.GetEnabledMevRelays() for _, relay := range relays { - relayUrls = append(relayUrls, relay.Urls[currentNetwork]) + relayUrls = append(relayUrls, relay.Urls[networkName]) } if cfg.CustomRelays.Value != "" { relayUrls = append(relayUrls, cfg.CustomRelays.Value) @@ -386,9 +389,9 @@ func createDefaultRelays() []MevRelay { ID: MevRelayID_Flashbots, Name: "Flashbots", Description: "Flashbots is the developer of MEV-Boost, and one of the best-known and most trusted relays in the space.", - Urls: map[config.Network]string{ - config.Network_Mainnet: "https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net?id=hyperdrive", - config.Network_Holesky: "https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-holesky.flashbots.net?id=hyperdrive", + Urls: map[string]string{ + config.EthNetwork_Mainnet: "https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net?id=hyperdrive", + config.EthNetwork_Holesky: "https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-holesky.flashbots.net?id=hyperdrive", }, }, @@ -397,9 +400,9 @@ func createDefaultRelays() []MevRelay { ID: MevRelayID_BloxrouteMaxProfit, Name: "bloXroute Max Profit", Description: "Select this to enable the \"max profit\" relay from bloXroute.", - Urls: map[config.Network]string{ - config.Network_Mainnet: "https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com?id=hyperdrive", - config.Network_Holesky: "https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.holesky.blxrbdn.com", + Urls: map[string]string{ + config.EthNetwork_Mainnet: "https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com?id=hyperdrive", + config.EthNetwork_Holesky: "https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.holesky.blxrbdn.com", }, }, @@ -408,19 +411,8 @@ func createDefaultRelays() []MevRelay { ID: MevRelayID_BloxrouteRegulated, Name: "bloXroute Regulated", Description: "Select this to enable the \"regulated\" relay from bloXroute.", - Urls: map[config.Network]string{ - config.Network_Mainnet: "https://0xb0b07cd0abef743db4260b0ed50619cf6ad4d82064cb4fbec9d3ec530f7c5e6793d9f286c4e082c0244ffb9f2658fe88@bloxroute.regulated.blxrbdn.com?id=hyperdrive", - }, - }, - - // Eden - { - ID: MevRelayID_Eden, - Name: "Eden Network", - Description: "Eden Network is the home of Eden Relay, a block building hub focused on optimising block rewards for validators.", - Urls: map[config.Network]string{ - config.Network_Mainnet: "https://0xb3ee7afcf27f1f1259ac1787876318c6584ee353097a50ed84f51a1f21a323b3736f271a895c7ce918c038e4265918be@relay.edennetwork.io?id=hyperdrive", - config.Network_Holesky: "https://0xb1d229d9c21298a87846c7022ebeef277dfc321fe674fa45312e20b5b6c400bfde9383f801848d7837ed5fc449083a12@relay-holesky.edennetwork.io?id=hyperdrive", + Urls: map[string]string{ + config.EthNetwork_Mainnet: "https://0xb0b07cd0abef743db4260b0ed50619cf6ad4d82064cb4fbec9d3ec530f7c5e6793d9f286c4e082c0244ffb9f2658fe88@bloxroute.regulated.blxrbdn.com?id=hyperdrive", }, }, @@ -429,8 +421,8 @@ func createDefaultRelays() []MevRelay { ID: MevRelayID_TitanRegional, Name: "Titan Regional", Description: "Titan Relay is a neutral, Rust-based MEV-Boost Relay optimized for low latency through put, geographical distribution, and robustness. This is the regulated (censoring) version.", - Urls: map[config.Network]string{ - config.Network_Mainnet: "https://0x8c4ed5e24fe5c6ae21018437bde147693f68cda427cd1122cf20819c30eda7ed74f72dece09bb313f2a1855595ab677d@regional.titanrelay.xyz", + Urls: map[string]string{ + config.EthNetwork_Mainnet: "https://0x8c4ed5e24fe5c6ae21018437bde147693f68cda427cd1122cf20819c30eda7ed74f72dece09bb313f2a1855595ab677d@regional.titanrelay.xyz", }, }, } diff --git a/shared/config/resources.go b/shared/config/resources.go index 7c26fe0..e4b5515 100644 --- a/shared/config/resources.go +++ b/shared/config/resources.go @@ -1,48 +1,114 @@ package config import ( + "errors" "fmt" + "io/fs" + "os" + "path/filepath" "github.com/rocket-pool/node-manager-core/config" + "gopkg.in/yaml.v3" ) +const ( + // The nodeset.io URL for production usage + NodesetUrlProd string = "https://nodeset.io/api" + + // The nodeset.io URL for development / staging + NodesetUrlStaging string = "https://staging.nodeset.io/api" + + // The deployment name for Mainnet + NodesetDeploymentMainnet string = "mainnet" + + // The deployment name for Holesky testing + NodesetDeploymentHolesky string = "holesky" +) + +var ( + // Mainnet resources for reference in testing + MainnetResourcesReference *HyperdriveResources = &HyperdriveResources{ + NodeSetApiUrl: NodesetUrlProd, + } + + // Holesky resources for reference in testing + HoleskyResourcesReference *HyperdriveResources = &HyperdriveResources{ + NodeSetApiUrl: NodesetUrlProd, + } + + // Devnet resources for reference in testing + HoleskyDevResourcesReference *HyperdriveResources = &HyperdriveResources{ + NodeSetApiUrl: NodesetUrlStaging, + } +) + +// Network settings with a field for Hyperdrive-specific settings +type HyperdriveSettings struct { + *config.NetworkSettings `yaml:",inline"` + + // Hyperdrive resources for the network + HyperdriveResources *HyperdriveResources `yaml:"hyperdriveResources" json:"hyperdriveResources"` +} + // A collection of network-specific resources and getters for them type HyperdriveResources struct { + // The URL for the NodeSet API server + NodeSetApiUrl string `yaml:"nodeSetApiUrl" json:"nodeSetApiUrl"` + + // The name of the deployment used by this instance of Hyperdrive + DeploymentName string `yaml:"deploymentName" json:"deploymentName"` +} + +// An aggregated collection of resources for the selected network, including Hyperdrive resources +type MergedResources struct { + // Base network resources *config.NetworkResources - // The URL for the NodeSet API server - NodeSetApiUrl string + // Hyperdrive resources + *HyperdriveResources } -// Creates a new resource collection for the given network -func NewHyperdriveResources(network config.Network) *HyperdriveResources { - // Mainnet - mainnetResources := &HyperdriveResources{ - NetworkResources: config.NewResources(config.Network_Mainnet), - NodeSetApiUrl: "https://nodeset.io/api", +// Load network settings from a folder +func LoadSettingsFiles(sourceDir string) ([]*HyperdriveSettings, error) { + // Make sure the folder exists + _, err := os.Stat(sourceDir) + if errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("network settings folder [%s] does not exist", sourceDir) } - // Holesky - holeskyResources := &HyperdriveResources{ - NetworkResources: config.NewResources(config.Network_Holesky), - NodeSetApiUrl: "https://nodeset.io/api", + // Enumerate the dir + files, err := os.ReadDir(sourceDir) + if err != nil { + return nil, fmt.Errorf("error enumerating override source folder: %w", err) } - // Holesky Dev - holeskyDevResources := &HyperdriveResources{ - NetworkResources: config.NewResources(config.Network_Holesky), - NodeSetApiUrl: "https://staging.nodeset.io/api", - } - holeskyDevResources.Network = Network_HoleskyDev - - switch network { - case config.Network_Mainnet: - return mainnetResources - case config.Network_Holesky: - return holeskyResources - case Network_HoleskyDev: - return holeskyDevResources - } + settingsList := []*HyperdriveSettings{} + for _, file := range files { + // Ignore dirs and nonstandard files + if file.IsDir() || !file.Type().IsRegular() { + continue + } + + // Load the file + filename := file.Name() + ext := filepath.Ext(filename) + if ext != ".yaml" && ext != ".yml" { + // Only load YAML files + continue + } + settingsFilePath := filepath.Join(sourceDir, filename) + bytes, err := os.ReadFile(settingsFilePath) + if err != nil { + return nil, fmt.Errorf("error reading network settings file [%s]: %w", settingsFilePath, err) + } - panic(fmt.Sprintf("network %s is not supported", network)) + // Unmarshal the settings + settings := new(HyperdriveSettings) + err = yaml.Unmarshal(bytes, settings) + if err != nil { + return nil, fmt.Errorf("error unmarshalling network settings file [%s]: %w", settingsFilePath, err) + } + settingsList = append(settingsList, settings) + } + return settingsList, nil } diff --git a/shared/types/api/nodeset_constellation.go b/shared/types/api/nodeset_constellation.go new file mode 100644 index 0000000..f86c99a --- /dev/null +++ b/shared/types/api/nodeset_constellation.go @@ -0,0 +1,42 @@ +package api + +import ( + "github.com/ethereum/go-ethereum/common" + v2constellation "github.com/nodeset-org/nodeset-client-go/api-v2/constellation" + nscommon "github.com/nodeset-org/nodeset-client-go/common" +) + +type NodeSetConstellation_GetRegisteredAddressData struct { + NotRegisteredWithNodeSet bool `json:"notRegisteredWithNodeSet"` + NotRegisteredWithConstellation bool `json:"notRegisteredWithConstellation"` + RegisteredAddress common.Address `json:"registeredAddress"` +} + +type NodeSetConstellation_GetRegistrationSignatureData struct { + NotRegistered bool `json:"notRegistered"` + NotAuthorized bool `json:"notAuthorized"` + Signature []byte `json:"signature"` +} + +type NodeSetConstellation_GetDepositSignatureData struct { + NotRegistered bool `json:"notRegistered"` + NotAuthorized bool `json:"notAuthorized"` + LimitReached bool `json:"limitReached"` + MissingExitMessage bool `json:"missingExitMessage"` + Signature []byte `json:"signature"` +} + +type NodeSetConstellation_GetValidatorsData struct { + NotRegistered bool `json:"notRegistered"` + NotAuthorized bool `json:"notAuthorized"` + Validators []v2constellation.ValidatorStatus `json:"validators"` +} + +type NodeSetConstellation_UploadSignedExitsRequestBody struct { + ExitMessages []nscommon.ExitData `json:"exitMessages"` +} + +type NodeSetConstellation_UploadSignedExitsData struct { + NotRegistered bool `json:"notRegistered"` + NotAuthorized bool `json:"notAuthorized"` +} diff --git a/shared/types/api/nodeset_stakewise.go b/shared/types/api/nodeset_stakewise.go index 38f50d0..0f4f2ec 100644 --- a/shared/types/api/nodeset_stakewise.go +++ b/shared/types/api/nodeset_stakewise.go @@ -1,13 +1,15 @@ package api import ( - apiv1 "github.com/nodeset-org/nodeset-client-go/api-v1" + "github.com/ethereum/go-ethereum/common" + nscommon "github.com/nodeset-org/nodeset-client-go/common" + "github.com/nodeset-org/nodeset-client-go/common/stakewise" "github.com/rocket-pool/node-manager-core/beacon" ) type NodeSetStakeWise_GetRegisteredValidatorsData struct { - NotRegistered bool `json:"notRegistered"` - Validators []apiv1.ValidatorStatus `json:"validators"` + NotRegistered bool `json:"notRegistered"` + Validators []stakewise.ValidatorStatus `json:"validators"` } type NodeSetStakeWise_GetDepositDataSetVersionData struct { @@ -21,12 +23,22 @@ type NodeSetStakeWise_GetDepositDataSetData struct { DepositData []beacon.ExtendedDepositData `json:"depositData"` } +type NodeSetStakeWise_UploadDepositDataRequestBody struct { + Vault common.Address `json:"vault"` + DepositData []beacon.ExtendedDepositData `json:"depositData"` +} + type NodeSetStakeWise_UploadDepositDataData struct { NotRegistered bool `json:"notRegistered"` VaultNotFound bool `json:"vaultNotFound"` InvalidPermissions bool `json:"invalidPermissions"` } +type NodeSetStakeWise_UploadSignedExitsRequestBody struct { + Vault common.Address `json:"vault"` + ExitData []nscommon.ExitData `json:"exitData"` +} + type NodeSetStakeWise_UploadSignedExitsData struct { NotRegistered bool `json:"notRegistered"` } diff --git a/shared/types/api/service.go b/shared/types/api/service.go index af18638..06ab081 100644 --- a/shared/types/api/service.go +++ b/shared/types/api/service.go @@ -2,6 +2,7 @@ package api import ( "github.com/ethereum/go-ethereum/common" + "github.com/nodeset-org/hyperdrive-daemon/shared/config" "github.com/rocket-pool/node-manager-core/api/types" ) @@ -18,6 +19,14 @@ type ServiceClientStatusData struct { BcManagerStatus types.ClientManagerStatus `json:"bcManagerStatus"` } +type ServiceGetResourcesData struct { + Resources *config.MergedResources `json:"resources"` +} + +type ServiceGetNetworkSettingsData struct { + Settings *config.HyperdriveSettings `json:"settings"` +} + type ServiceGetConfigData struct { Config map[string]any `json:"config"` } diff --git a/shared/version.go b/shared/version.go index 2961631..30fe5eb 100644 --- a/shared/version.go +++ b/shared/version.go @@ -1,5 +1,5 @@ package shared const ( - HyperdriveVersion string = "1.1.0-dev" + HyperdriveVersion string = "1.1.0-b1" ) diff --git a/tasks/run.go b/tasks/run.go index 5582961..0eac321 100644 --- a/tasks/run.go +++ b/tasks/run.go @@ -36,7 +36,7 @@ type TaskLoop struct { // Services ctx context.Context logger *log.Logger - sp *common.ServiceProvider + sp common.IHyperdriveServiceProvider wg *sync.WaitGroup // Internal @@ -44,7 +44,7 @@ type TaskLoop struct { wasBeaconClientSynced bool } -func NewTaskLoop(sp *common.ServiceProvider, wg *sync.WaitGroup) *TaskLoop { +func NewTaskLoop(sp common.IHyperdriveServiceProvider, wg *sync.WaitGroup) *TaskLoop { logger := sp.GetTasksLogger() ctx := logger.CreateContextWithLogger(sp.GetBaseContext()) taskLoop := &TaskLoop{ diff --git a/testing/node.go b/testing/node.go new file mode 100644 index 0000000..d6b6793 --- /dev/null +++ b/testing/node.go @@ -0,0 +1,123 @@ +package testing + +import ( + "fmt" + "log/slog" + "net/url" + "os" + "path/filepath" + "sync" + + "github.com/nodeset-org/hyperdrive-daemon/client" + "github.com/nodeset-org/hyperdrive-daemon/common" + "github.com/nodeset-org/hyperdrive-daemon/server" + "github.com/nodeset-org/hyperdrive-daemon/shared/config" + hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" +) + +// A complete Hyperdrive node instance +type HyperdriveNode struct { + // The daemon's service provider + sp common.IHyperdriveServiceProvider + + // The daemon's HTTP API server + serverMgr *server.ServerManager + + // An HTTP API client for the daemon + client *client.ApiClient + + // The client logger + logger *slog.Logger + + // Wait group for graceful shutdown + wg *sync.WaitGroup +} + +// Create a new Hyperdrive node, including its folder structure, service provider, server manager, and API client. +func newHyperdriveNode(sp common.IHyperdriveServiceProvider, address string, clientLogger *slog.Logger) (*HyperdriveNode, error) { + // Create the server + wg := &sync.WaitGroup{} + cfg := sp.GetConfig() + serverMgr, err := server.NewServerManager(sp, address, cfg.ApiPort.Value, wg) + if err != nil { + return nil, fmt.Errorf("error creating hyperdrive server: %v", err) + } + + // Create the client + urlString := fmt.Sprintf("http://%s:%d/%s", address, serverMgr.GetPort(), config.HyperdriveApiClientRoute) + url, err := url.Parse(urlString) + if err != nil { + return nil, fmt.Errorf("error parsing client URL [%s]: %v", urlString, err) + } + apiClient := client.NewApiClient(url, clientLogger, nil) + + return &HyperdriveNode{ + sp: sp, + serverMgr: serverMgr, + client: apiClient, + logger: clientLogger, + wg: wg, + }, nil +} + +// Closes the Constellation node and its Hyperdrive parent. +func (n *HyperdriveNode) Close() error { + if n.serverMgr != nil { + n.serverMgr.Stop() + n.wg.Wait() + n.serverMgr = nil + n.logger.Info("Stopped Hyperdrive daemon API server") + } + return nil +} + +// Get the daemon's service provider +func (n *HyperdriveNode) GetServiceProvider() common.IHyperdriveServiceProvider { + return n.sp +} + +// Get the HTTP API server for the node's daemon +func (n *HyperdriveNode) GetServerManager() *server.ServerManager { + return n.serverMgr +} + +// Get the HTTP API client for interacting with the node's daemon server +func (n *HyperdriveNode) GetApiClient() *client.ApiClient { + return n.client +} + +// Create a new Hyperdrive node based on this one's configuration, but with a custom folder, address, and port. +func (n *HyperdriveNode) CreateSubNode(folder string, address string, port uint16) (*HyperdriveNode, error) { + // Make a new config + parentSp := n.sp + parentHdCfg := parentSp.GetConfig() + hdNetSettings := parentHdCfg.GetNetworkSettings() + cfg, err := hdconfig.NewHyperdriveConfigForNetwork(folder, hdNetSettings, parentHdCfg.Network.Value) + if err != nil { + return nil, fmt.Errorf("error creating Hyperdrive config: %v", err) + } + cfg.UserDataPath.Value = filepath.Join(folder, "data") + cfg.ApiPort.Value = port + + // Make sure the data and modules directories exist + dataDir := cfg.UserDataPath.Value + moduleDir := filepath.Join(dataDir, hdconfig.ModulesName) + err = os.MkdirAll(moduleDir, 0755) + if err != nil { + return nil, fmt.Errorf("error creating data and modules directories [%s]: %v", moduleDir, err) + } + + // Make a new service provider + sp, err := common.NewHyperdriveServiceProviderFromCustomServices( + cfg, + parentSp.GetResources(), + parentSp.GetEthClient(), + parentSp.GetBeaconClient(), + parentSp.GetDocker(), + ) + if err != nil { + return nil, fmt.Errorf("error creating Hyperdrive service provider: %v", err) + } + + return newHyperdriveNode(sp, address, n.logger) +} diff --git a/testing/test-manager.go b/testing/test-manager.go index af65d82..2124e1e 100644 --- a/testing/test-manager.go +++ b/testing/test-manager.go @@ -2,57 +2,55 @@ package testing import ( "fmt" - "net/url" "os" "path/filepath" "sync" "time" - "github.com/nodeset-org/hyperdrive-daemon/client" "github.com/nodeset-org/hyperdrive-daemon/common" - "github.com/nodeset-org/hyperdrive-daemon/server" hdconfig "github.com/nodeset-org/hyperdrive-daemon/shared/config" nsserver "github.com/nodeset-org/nodeset-client-go/server-mock/server" "github.com/nodeset-org/osha" + "github.com/rocket-pool/node-manager-core/config" "github.com/rocket-pool/node-manager-core/log" "github.com/rocket-pool/node-manager-core/node/services" ) +// A custom provisioning function that can alter or update the network settings used by the test manager prior to starting the Hyperdrive daemon +type NetworkSettingsProvisioner func(*config.NetworkSettings) *config.NetworkSettings + // HyperdriveTestManager provides bootstrapping and a test service provider, useful for testing type HyperdriveTestManager struct { *osha.TestManager - // The service provider for the test environment - serviceProvider *common.ServiceProvider + // The Hyperdrive node owned by this test manager + node *HyperdriveNode // The mock for the nodeset.io service nodesetMock *nsserver.NodeSetMockServer - // The Hyperdrive Daemon server - serverMgr *server.ServerManager + // Snapshot ID from the baseline - the initial state of the nodeset.io service prior to running any tests + baselineSnapshotID string - // The Hyperdrive Daemon client - apiClient *client.ApiClient + // Map of which services were captured during a snapshot + snapshotServiceMap map[string]Service // Wait groups for graceful shutdown - hdWg *sync.WaitGroup nsWg *sync.WaitGroup } // Creates a new HyperdriveTestManager instance. Requires management of your own nodeset.io server mock. // `address` is the address to bind the Hyperdrive daemon to. -func NewHyperdriveTestManager(address string, cfg *hdconfig.HyperdriveConfig, resources *hdconfig.HyperdriveResources, nsServer *nsserver.NodeSetMockServer, nsWaitGroup *sync.WaitGroup) (*HyperdriveTestManager, error) { +func NewHyperdriveTestManager(address string, port uint, cfg *hdconfig.HyperdriveConfig, resources *hdconfig.MergedResources, nsServer *nsserver.NodeSetMockServer) (*HyperdriveTestManager, error) { tm, err := osha.NewTestManager() if err != nil { return nil, fmt.Errorf("error creating test manager: %w", err) } - return newHyperdriveTestManagerImpl(address, tm, cfg, resources, nsServer, nsWaitGroup) + return newHyperdriveTestManagerImpl(address, tm, cfg, resources, nsServer, nil) } // Creates a new HyperdriveTestManager instance with default test artifacts. -// `hyperdriveAddress` is the address to bind the Hyperdrive daemon to. -// `nodesetAddress` is the address to bind the nodeset.io server to. -func NewHyperdriveTestManagerWithDefaults(hyperdriveAddress string, nodesetAddress string) (*HyperdriveTestManager, error) { +func NewHyperdriveTestManagerWithDefaults(netSettingsProvisioner NetworkSettingsProvisioner) (*HyperdriveTestManager, error) { tm, err := osha.NewTestManager() if err != nil { return nil, fmt.Errorf("error creating test manager: %w", err) @@ -60,7 +58,7 @@ func NewHyperdriveTestManagerWithDefaults(hyperdriveAddress string, nodesetAddre // Make the nodeset.io mock server nsWg := &sync.WaitGroup{} - nodesetMock, err := nsserver.NewNodeSetMockServer(tm.GetLogger(), nodesetAddress, 0) + nodesetMock, err := nsserver.NewNodeSetMockServer(tm.GetLogger(), "localhost", 0) if err != nil { closeTestManager(tm) return nil, fmt.Errorf("error creating nodeset mock server: %v", err) @@ -74,23 +72,34 @@ func NewHyperdriveTestManagerWithDefaults(hyperdriveAddress string, nodesetAddre // Make a new Hyperdrive config testDir := tm.GetTestDir() beaconCfg := tm.GetBeaconMockManager().GetConfig() - resources := GetTestResources(beaconCfg, fmt.Sprintf("http://%s:%d/api/", nodesetAddress, nodesetMock.GetPort())) - cfg := hdconfig.NewHyperdriveConfigForNetwork(testDir, hdconfig.Network_LocalTest, resources) + networkSettings := GetDefaultTestNetworkSettings(beaconCfg) + networkSettings = netSettingsProvisioner(networkSettings) + resources := getTestResources(networkSettings.NetworkResources, fmt.Sprintf("http://%s:%d/api/", "localhost", nodesetMock.GetPort()), "localtest") + hdNetSettings := &hdconfig.HyperdriveSettings{ + NetworkSettings: networkSettings, + HyperdriveResources: resources.HyperdriveResources, + } + cfg, err := hdconfig.NewHyperdriveConfigForNetwork(testDir, []*hdconfig.HyperdriveSettings{hdNetSettings}, hdconfig.Network_LocalTest) + if err != nil { + closeTestManager(tm) + return nil, fmt.Errorf("error creating Hyperdrive config: %v", err) + } cfg.Network.Value = hdconfig.Network_LocalTest + cfg.ApiPort.Value = 0 // Make the test manager - return newHyperdriveTestManagerImpl(hyperdriveAddress, tm, cfg, resources, nodesetMock, nsWg) + return newHyperdriveTestManagerImpl("localhost", tm, cfg, resources, nodesetMock, nsWg) } // Implementation for creating a new HyperdriveTestManager -func newHyperdriveTestManagerImpl(address string, tm *osha.TestManager, cfg *hdconfig.HyperdriveConfig, resources *hdconfig.HyperdriveResources, nsServer *nsserver.NodeSetMockServer, nsWaitGroup *sync.WaitGroup) (*HyperdriveTestManager, error) { +func newHyperdriveTestManagerImpl(address string, tm *osha.TestManager, cfg *hdconfig.HyperdriveConfig, resources *hdconfig.MergedResources, nsServer *nsserver.NodeSetMockServer, nsWaitGroup *sync.WaitGroup) (*HyperdriveTestManager, error) { // Make managers beaconCfg := tm.GetBeaconMockManager().GetConfig() ecManager := services.NewExecutionClientManager(tm.GetExecutionClient(), uint(beaconCfg.ChainID), time.Minute) bnManager := services.NewBeaconClientManager(tm.GetBeaconClient(), uint(beaconCfg.ChainID), time.Minute) // Make a new service provider - serviceProvider, err := common.NewServiceProviderFromCustomServices( + serviceProvider, err := common.NewHyperdriveServiceProviderFromCustomServices( cfg, resources, ecManager, @@ -111,72 +120,56 @@ func newHyperdriveTestManagerImpl(address string, tm *osha.TestManager, cfg *hdc return nil, fmt.Errorf("error creating data and modules directories [%s]: %v", moduleDir, err) } - // Create the server - hdWg := &sync.WaitGroup{} - serverMgr, err := server.NewServerManager(serviceProvider, address, 0, hdWg) + // Make the Hyperdrive node + node, err := newHyperdriveNode(serviceProvider, address, tm.GetLogger()) if err != nil { closeTestManager(tm) - return nil, fmt.Errorf("error creating hyperdrive server: %v", err) + return nil, fmt.Errorf("error creating Hyperdrive node: %v", err) } - // Create the client - urlString := fmt.Sprintf("http://%s:%d/%s", address, serverMgr.GetPort(), hdconfig.HyperdriveApiClientRoute) - url, err := url.Parse(urlString) - if err != nil { - closeTestManager(tm) - return nil, fmt.Errorf("error parsing client URL [%s]: %v", urlString, err) - } - apiClient := client.NewApiClient(url, tm.GetLogger(), nil) - // Return m := &HyperdriveTestManager{ - TestManager: tm, - serviceProvider: serviceProvider, - nodesetMock: nsServer, - serverMgr: serverMgr, - apiClient: apiClient, - hdWg: hdWg, - nsWg: nsWaitGroup, + TestManager: tm, + node: node, + nodesetMock: nsServer, + nsWg: nsWaitGroup, + snapshotServiceMap: map[string]Service{}, } - return m, nil -} - -// Returns the service provider for the test environment -func (m *HyperdriveTestManager) GetServiceProvider() *common.ServiceProvider { - return m.serviceProvider -} - -// Get the nodeset.io mock server -func (m *HyperdriveTestManager) GetNodeSetMockServer() *nsserver.NodeSetMockServer { - return m.nodesetMock -} -// Returns the Hyperdrive Daemon server -func (m *HyperdriveTestManager) GetServerManager() *server.ServerManager { - return m.serverMgr -} + // Create the baseline snapshot + baselineSnapshotID, err := m.takeSnapshot(Service_All) + if err != nil { + return nil, fmt.Errorf("error creating baseline snapshot: %w", err) + } + m.baselineSnapshotID = baselineSnapshotID -// Returns the Hyperdrive Daemon client -func (m *HyperdriveTestManager) GetApiClient() *client.ApiClient { - return m.apiClient + return m, nil } // Closes the Hyperdrive test manager, shutting down the daemon func (m *HyperdriveTestManager) Close() error { if m.nodesetMock != nil { - err := m.nodesetMock.Stop() - if err != nil { - m.GetLogger().Warn("WARNING: nodeset server mock didn't shutdown cleanly", log.Err(err)) + // Check if we're managing the service - if so just stop it + if m.nsWg != nil { + err := m.nodesetMock.Stop() + if err != nil { + m.GetLogger().Warn("WARNING: nodeset server mock didn't shutdown cleanly", log.Err(err)) + } + m.nsWg.Wait() + m.TestManager.GetLogger().Info("Stopped nodeset.io mock server") + } else { + err := m.nodesetMock.GetManager().RevertToSnapshot(m.baselineSnapshotID) + if err != nil { + m.GetLogger().Warn("WARNING: error reverting nodeset server mock to baseline", log.Err(err)) + } else { + m.TestManager.GetLogger().Info("Reverted nodeset.io mock server to baseline snapshot") + } } - m.nsWg.Wait() - m.TestManager.GetLogger().Info("Stopped nodeset.io mock server") m.nodesetMock = nil } - if m.serverMgr != nil { - m.serverMgr.Stop() - m.hdWg.Wait() - m.TestManager.GetLogger().Info("Stopped daemon API server") - m.serverMgr = nil + err := m.node.Close() + if err != nil { + return fmt.Errorf("error closing Hyperdrive node: %w", err) } if m.TestManager != nil { err := m.TestManager.Close() @@ -186,10 +179,91 @@ func (m *HyperdriveTestManager) Close() error { return nil } +// =============== +// === Getters === +// =============== + +// Get the Hyperdrive node +func (m *HyperdriveTestManager) GetNode() *HyperdriveNode { + return m.node +} + +// Get the nodeset.io mock server +func (m *HyperdriveTestManager) GetNodeSetMockServer() *nsserver.NodeSetMockServer { + return m.nodesetMock +} + +// ==================== +// === Snapshotting === +// ==================== + +// Reverts the services to the baseline snapshot +func (m *HyperdriveTestManager) RevertToBaseline() error { + err := m.TestManager.RevertToBaseline() + if err != nil { + return fmt.Errorf("error reverting to baseline snapshot: %w", err) + } + + // Regenerate the baseline snapshot since Hardhat can't revert to it multiple times + baselineSnapshotID, err := m.takeSnapshot(Service_All) + if err != nil { + return fmt.Errorf("error creating baseline snapshot: %w", err) + } + m.baselineSnapshotID = baselineSnapshotID + return nil +} + +// Takes a snapshot of the service states +func (m *HyperdriveTestManager) CreateCustomSnapshot(services Service) (string, error) { + return m.takeSnapshot(services) +} + +// Revert the services to a snapshot state +func (m *HyperdriveTestManager) RevertToCustomSnapshot(snapshotID string) error { + return m.revertToSnapshot(snapshotID) +} + // ========================== // === Internal Functions === // ========================== +// Takes a snapshot of the service states +func (m *HyperdriveTestManager) takeSnapshot(services Service) (string, error) { + // Run the parent snapshotter + parentServices := osha.Service(services) + snapshotName, err := m.TestManager.CreateCustomSnapshot(parentServices) + if err != nil { + return "", fmt.Errorf("error taking snapshot: %w", err) + } + + // Snapshot the nodeset.io mock + if services.Contains(Service_NodeSet) { + m.nodesetMock.GetManager().TakeSnapshot(snapshotName) + } + + // Store the services that were captured + m.snapshotServiceMap[snapshotName] = services + return snapshotName, nil +} + +// Revert the services to a snapshot state +func (m *HyperdriveTestManager) revertToSnapshot(snapshotID string) error { + services, exists := m.snapshotServiceMap[snapshotID] + if !exists { + return fmt.Errorf("snapshot with ID [%s] does not exist", snapshotID) + } + + // Revert the nodeset.io mock + if services.Contains(Service_NodeSet) { + err := m.nodesetMock.GetManager().RevertToSnapshot(snapshotID) + if err != nil { + return fmt.Errorf("error reverting the nodeset.io mock to snapshot %s: %w", snapshotID, err) + } + } + + return m.TestManager.RevertToCustomSnapshot(snapshotID) +} + // Closes the OSHA test manager, logging any errors func closeTestManager(tm *osha.TestManager) { err := tm.Close() diff --git a/testing/test-resources.go b/testing/test-resources.go index ec14c4b..b95e4dd 100644 --- a/testing/test-resources.go +++ b/testing/test-resources.go @@ -6,15 +6,33 @@ import ( "github.com/rocket-pool/node-manager-core/config" ) -// Returns a network resources instance with local testing network values -func GetTestResources(beaconConfig *db.Config, nodesetUrl string) *hdconfig.HyperdriveResources { - return &hdconfig.HyperdriveResources{ +const ( + TestNetworkEthName string = "hardhat" +) + +// Creates a new set of network settings designed for usage in local testing with Hardat. +// The settings are incomplete; things like the multicall address and balance batcher address should be set by the user if needed. +func GetDefaultTestNetworkSettings(beaconConfig *db.Config) *config.NetworkSettings { + return &config.NetworkSettings{ + Key: hdconfig.Network_LocalTest, + Name: "Local Test Network", + Description: "Local test network for development and testing", NetworkResources: &config.NetworkResources{ - Network: hdconfig.Network_LocalTest, - EthNetworkName: "localtest", + EthNetworkName: TestNetworkEthName, ChainID: uint(beaconConfig.ChainID), GenesisForkVersion: beaconConfig.GenesisForkVersion, }, - NodeSetApiUrl: nodesetUrl, + DefaultConfigSettings: map[string]any{}, + } +} + +// Returns a network resources instance with local testing network values +func getTestResources(networkResources *config.NetworkResources, nodesetUrl string, deploymentName string) *hdconfig.MergedResources { + return &hdconfig.MergedResources{ + NetworkResources: networkResources, + HyperdriveResources: &hdconfig.HyperdriveResources{ + NodeSetApiUrl: nodesetUrl, + DeploymentName: deploymentName, + }, } } diff --git a/testing/types.go b/testing/types.go new file mode 100644 index 0000000..4583124 --- /dev/null +++ b/testing/types.go @@ -0,0 +1,26 @@ +package testing + +// Service represents a service provided by OSHA +type Service int + +const ( + // Represents the Execution client and Beacon node services + Service_EthClients Service = 1 << iota + + // Represents the Docker client and compose services + Service_Docker + + // Represents the underlying filesystem + Service_Filesystem + + // Represents the nodeset.io service + Service_NodeSet + + // Represents all of the services provided by OSHA + Service_All Service = Service_EthClients | Service_Docker | Service_Filesystem | Service_NodeSet +) + +// Check if a service value contains a specific service flag +func (s Service) Contains(service Service) bool { + return s&service == service +}