From 1ab56c16e06eecd2e924812f97c4da0968cda9c8 Mon Sep 17 00:00:00 2001 From: pablomendezroyo Date: Wed, 9 Aug 2023 18:17:45 +0200 Subject: [PATCH 01/45] add rollups tab --- .../src/components/sidebar/navbarItems.ts | 7 +++ packages/admin-ui/src/pages/index.ts | 2 + .../src/pages/rollups/components/Optimism.tsx | 5 ++ .../src/pages/rollups/components/Polygon.tsx | 5 ++ .../pages/rollups/components/RollupsRoot.tsx | 58 +++++++++++++++++ packages/admin-ui/src/pages/rollups/data.ts | 62 +++++++++++++++++++ packages/admin-ui/src/pages/rollups/index.ts | 4 ++ 7 files changed, 143 insertions(+) create mode 100644 packages/admin-ui/src/pages/rollups/components/Optimism.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/Polygon.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx create mode 100644 packages/admin-ui/src/pages/rollups/data.ts create mode 100644 packages/admin-ui/src/pages/rollups/index.ts diff --git a/packages/admin-ui/src/components/sidebar/navbarItems.ts b/packages/admin-ui/src/components/sidebar/navbarItems.ts index 609e7a449..bdf390fb4 100644 --- a/packages/admin-ui/src/components/sidebar/navbarItems.ts +++ b/packages/admin-ui/src/components/sidebar/navbarItems.ts @@ -17,6 +17,7 @@ import { } from "react-icons/md"; import { SiEthereum } from "react-icons/si"; import { BiGitRepoForked } from "react-icons/bi"; +import { GiRolledCloth } from "react-icons/gi"; // URLs import { relativePath as dashboardRelativePath } from "pages/dashboard"; import { relativePath as devicesRelativePath } from "pages/vpn"; @@ -28,6 +29,7 @@ import { relativePath as supportRelativePath } from "pages/support"; import { relativePath as wifiRelativePath } from "pages/wifi"; import { relativePath as communityRelativePath } from "pages/community"; import { relativePath as stakersRelativePath } from "pages/stakers"; +import { relativePath as rollupsRelativePath } from "pages/rollups"; import { relativePath as repositoryRelativePath } from "pages/repository"; export const fundedBy: { logo: string; text: string; link: string }[] = [ @@ -111,6 +113,11 @@ export const basicItems: { href: stakersRelativePath, icon: SiEthereum }, + { + name: "Rollups", + href: rollupsRelativePath, + icon: GiRolledCloth + }, { name: "Repository", href: repositoryRelativePath, diff --git a/packages/admin-ui/src/pages/index.ts b/packages/admin-ui/src/pages/index.ts index 66011b0ac..5534631d3 100644 --- a/packages/admin-ui/src/pages/index.ts +++ b/packages/admin-ui/src/pages/index.ts @@ -8,6 +8,7 @@ import * as system from "./system"; import * as wifi from "./wifi"; import * as community from "./community"; import * as stakers from "./stakers"; +import * as rollups from "./rollups"; import * as repository from "./repository"; export const pages = { @@ -17,6 +18,7 @@ export const pages = { installer, packages, stakers, + rollups, sdk, support, community, diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx new file mode 100644 index 000000000..d551fbe68 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function Optimism() { + return
Optimism
; +} diff --git a/packages/admin-ui/src/pages/rollups/components/Polygon.tsx b/packages/admin-ui/src/pages/rollups/components/Polygon.tsx new file mode 100644 index 000000000..0df36731e --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/Polygon.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function Polygon() { + return
Polygon
; +} diff --git a/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx new file mode 100644 index 000000000..468acbcb9 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import Optimism from "./Optimism"; +import Polygon from "./Polygon"; +import Title from "components/Title"; +import { NavLink, Routes, Route } from "react-router-dom"; +import { title } from "../data"; + +const RollupsRoot: React.FC = () => { + const rollupsItems: { + subPath: string; + title: string; + component: () => React.JSX.Element; + }[] = [ + { + subPath: "optimism", + title: "Optimism", + component: () => Optimism() + }, + { + subPath: "polygon", + title: "Polygon", + component: () => Polygon() + } + ]; + + return ( + <> + + <div className="horizontal-navbar"> + {rollupsItems.map(route => ( + <button key={route.subPath} className="item-container"> + <NavLink + to={route.subPath} + className="item no-a-style" + style={{ whiteSpace: "nowrap" }} + > + {route.title} + </NavLink> + </button> + ))} + </div> + + <div className="section-spacing"> + <Routes> + {rollupsItems.map(route => ( + <Route + key={route.subPath} + path={route.subPath} + element={route.component()} + /> + ))} + </Routes> + </div> + </> + ); +}; + +export default RollupsRoot; diff --git a/packages/admin-ui/src/pages/rollups/data.ts b/packages/admin-ui/src/pages/rollups/data.ts new file mode 100644 index 000000000..b0f09138f --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/data.ts @@ -0,0 +1,62 @@ +export const relativePath = "rollups/optimism"; // default redirect to optimism +export const rootPath = "rollups/*"; +export const title = "Rollups"; +export const disclaimer = `## Terms of Use - DAppNode + +Effective as of Oct 14, 2020 + +By downloading, accessing or using this DAppNode Package (“DNP”), you (referenced herein as “you” or the “user”) certify that you have read and agreed to the terms and conditions below (the “Terms”) which form a binding contract between you and DAppNode Association (referenced herein as “we” or “us”). If you do not agree to the Terms, do not download or use DAppNode nor any of the DNPs. + +### About DAppNode + +DAppNode is Free Open Source Software (“FOSS”) aimed at facilitating the use of decentralized technologies. DAppNode is developed by the DAppNode Association, a voluntary association with the purpose of empowering users to participate in decentralized networks and to help such networks become more resilient by promoting the deployment of more nodes. +The modular structure of the DAppNode FOSS allows for the wrapping of 3rd party software with the intention of facilitating its deployment. In no way we are responsible for the misuse of such software and in no way we warrant its functionalities. We accept no responsibiity for its errors, or for the errors that the wrapping process might have introduced. Any usage of DAppNode and a DNP is strictly at your own risk. + +### About this DNP + +This software is experimental, presented ‘as is’ and inherently carries risks. By installing it, you acknowledge that DAppNode Association has done its best to mitigate these risks and accept to waive any liability or responsibility for DAppNode Association in case of any shortage, discrepancy, damage, loss or destruction of any digital asset managed within this DAppNode package. + +This package helps block proposers receive blocks from block producers to be proposed to the Ethereum network via relayers. Dappnode has no affiliation with any block producers nor relayers and is in no way responsible for the content of such blocks. The user is the sole responsible for complying with any regulations the user is subject to. + +This is experimental open source software released under an GPLv3 license and may contain errors and/or bugs. No guarantee or representations whatsoever is made regarding its suitability (or its use) for any purpose or regarding its compliance with any applicable laws and regulations. Use of the software is at your own risk and discretion and by using the software you acknowledge that you have read this disclaimer, understand its contents, assume all risk related thereto and hereby release, waive, discharge and covenant not to sue DAppNode Association or any officers, employees or affiliates from and for any direct or indirect liability resulting from the use of the software as permissible by applicable laws and regulations. + +### Licensing Terms + +DAppNode FOSS is a fully open-source software program licensed pursuant to the GNU General Public License v3.0. + +The DAppNode name, the term “DAppNode” and all related names, logos, product and service names, designs and slogans are trademarks of DAppNode Association or its affiliates and/or licensors. You must not use such marks without our prior written permission. + +### Risks of Operating DAppNode + +The use of DAppNode and the 3rd party software included within its different DNPs can lead to loss of money. Blockchain technologies and in particular Ethereum are still experimental systems and ETH and other cryptocurrencies remain a risky investment. You alone are responsible for your actions on DAppNode including the security of your cryptocurrency and meeting any applicable minimum system requirements. + +Use the 3rd party software included within its different DNPs might be subject to Terms and Conditions of the 3rd party. DAppNode association has done its best to include such Terms and Conditions, however we do not warrant the accuracy, completeness or usefulness of the 3rd party Terms and Conditions. Any reliance you place on such information is strictly at your own risk. We recommend consulting with the 3rd party responsible for the software for its most updated Terms and Conditions. + +We make no claims that DAppNode is appropriate or permitted for use in any specific jurisdiction. Access to DAppNode may not be legal by certain persons or in certain jurisdictions or countries. If you access DAppNode, you do so on your own initiative and are responsible for compliance with local laws. + +Some Internet plans will charge an additional amount for any excess upload bandwidth used that isn’t included in the plan and may terminate your connection without warning because of overuse. We advise that you check whether your Internet connection is subjected to such limitations and monitor your bandwidth use so that you can stop DAppNode before you reach your bandwidth limit. + +### Warranty Disclaimer + +DAPPNODE IS PROVIDED ON AN “AS-IS” BASIS AND MAY INCLUDE ERRORS, OMISSIONS, OR OTHER INACCURACIES. DAPPNODE ASSOCIATION AND ITS CONTRIBUTORS MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT DAPNODDE FOR ANY PURPOSE, AND HEREBY EXPRESSLY DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT OR ANY OTHER IMPLIED WARRANTY UNDER THE UNIFORM COMPUTER INFORMATION TRANSACTIONS ACT AS ENACTED BY ANY STATE. WE ALSO MAKE NO REPRESENTATIONS OR WARRANTIES THAT DAPPNODE WILL OPERATE ERROR-FREE, UNINTERRUPTED, OR IN A MANNER THAT WILL MEET YOUR REQUIREMENTS AND/OR NEEDS. THEREFORE, YOU ASSUME THE ENTIRE RISK REGARDING THE QUALITY AND/OR PERFORMANCE OF DAPPNODE AND ANY TRANSACTIONS ENTERED INTO THEREON. + +### Limitation of Liability + +In no event will DAppNode Association or any of its contributors be liable, whether in contract, warranty, tort (including negligence, whether active, passive or imputed), product liability, strict liability or other theory, breach of statutory duty or otherwise arising out of, or in connection with, your use of DAppNode, for any direct, indirect, incidental, special or consequential damages (including any loss of profits or data, business interruption or other pecuniary loss, or damage, loss or other compromise of data, in each case whether direct, indirect, incidental, special or consequential) arising out of the use of DAppNode, even if we or other users have been advised of the possibility of such damages. The foregoing limitations and disclaimers shall apply to the maximum extent permitted by applicable law, even if any remedy fails of its essential purpose. You acknowledge and agree that the limitations of liability afforded us hereunder constitute a material and actual inducement and condition to entering into these Terms, and are reasonable, fair and equitable in scope to protect our legitimate interests in light of the fact that we are not receiving consideration from you for providing DAppNode. + +### Indemnification + +To the maximum extent permitted by law, you will defend, indemnify and hold DAppNode and its contributors harmless from and against any and all claims, actions, suits, investigations, or proceedings by any third party (including any party or purported party to or beneficiary or purported beneficiary of any transaction on DAppNode), as well as any and all losses, liabilities, +damages, costs, and expenses (including reasonable attorneys’ fees) arising out of, accruing from, or in any way related to (i) your breach of the terms of this Agreement, (ii) any transaction, or the failure to occur of any transaction on DAppNode, and (iii) your negligence, fraud, or willful misconduct. + +### Compliance with Laws and Tax Obligations + +Your use of DAppNode is subject to all applicable laws of any governmental authority, including, without limitation, federal, state and foreign securities laws, tax laws, tariff and trade laws, ordinances, judgments, decrees, injunctions, writs and orders or like actions of any governmental authority and rules, regulations, orders, interpretations, licenses, and permits of any federal, regional, state, county, municipal or other governmental authority and you agree to comply with all such laws in your use of DAppNode. The users of DAppNode are solely responsible to determinate what, if any, taxes apply to their cryptocurrency transactions. The owners of, or contributors to, DAppNode are not responsible for determining the taxes that apply to cryptocurrency transactions. + +### Miscellaneous + +We reserve the right to revise these Terms, and your rights and obligations are at all times subject to the then-current Terms provided on DAppNode. Your continued use of DAppNode constitutes acceptance of such revised Terms. + +These Terms constitute the entire agreement between you and DAppNode Association regarding use of DAppNode FOSS and will supersede all prior agreements whether, written or oral. No usage of trade or other regular practice or method of dealing between the parties will be used to modify, interpret, supplement, or alter the terms of these Terms. + +If any portion of these Terms is held invalid or unenforceable, such invalidity or enforceability will not affect the other provisions of these Terms, which will remain in full force and effect, and the invalid or unenforceable portion will be given effect to the greatest extent possible. The failure of a party to require performance of any provision will not affect that party’s right to require performance at any time thereafter, nor will a waiver of any breach or default of these Terms or any provision of these Terms constitute a waiver of any subsequent breach or default or a waiver of the provision itself.`; diff --git a/packages/admin-ui/src/pages/rollups/index.ts b/packages/admin-ui/src/pages/rollups/index.ts new file mode 100644 index 000000000..aacf111af --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/index.ts @@ -0,0 +1,4 @@ +import RollupsRoot from "./components/RollupsRoot"; + +export { rootPath, relativePath } from "./data"; +export const RootComponent = RollupsRoot; From 04c2608ca77a381852a24689f812f4078c24e223 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Mon, 11 Sep 2023 12:06:22 +0200 Subject: [PATCH 02/45] add optimism db --- packages/dappmanager/src/db/index.ts | 1 + packages/dappmanager/src/db/optimism.ts | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 packages/dappmanager/src/db/optimism.ts diff --git a/packages/dappmanager/src/db/index.ts b/packages/dappmanager/src/db/index.ts index f3184959e..c5e8d69a9 100644 --- a/packages/dappmanager/src/db/index.ts +++ b/packages/dappmanager/src/db/index.ts @@ -8,6 +8,7 @@ export * from "./ipfsClient.js"; export * from "./fileTransferPath.js"; export * from "./network.js"; export * from "./notification.js"; +export * from "./optimism.js"; export * from "./package.js"; export * from "./registry.js"; export * from "./releaseKeys.js"; diff --git a/packages/dappmanager/src/db/optimism.ts b/packages/dappmanager/src/db/optimism.ts new file mode 100644 index 000000000..fa974aeeb --- /dev/null +++ b/packages/dappmanager/src/db/optimism.ts @@ -0,0 +1,20 @@ +import { dbMain } from "./dbFactory.js"; + +const OP_HISTORICAL_GETH = "op-historical-geth"; +const OP_HISTORICAL_ERIGON = "op-historical-erigon"; +const OP_EXECUTION_CLIENT = "op-execution-client"; + +export const opHistoricalGeth = dbMain.staticKey<boolean>( + OP_HISTORICAL_GETH, + false +); + +export const opHistoricalErigon = dbMain.staticKey<boolean>( + OP_HISTORICAL_ERIGON, + false +); + +export const opExecutionClient = dbMain.staticKey<string | null>( + OP_EXECUTION_CLIENT, + null +); From d0b57b0277f7fb5667bfbc37194c0a1eea257909 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Mon, 11 Sep 2023 12:10:17 +0200 Subject: [PATCH 03/45] bump types dep --- packages/admin-ui/package.json | 2 +- packages/common/package.json | 2 +- packages/dappmanager/package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json index ab4d09fd1..f4530aebf 100644 --- a/packages/admin-ui/package.json +++ b/packages/admin-ui/package.json @@ -23,7 +23,7 @@ "dependencies": { "@dappnode/common": "^0.1.0", "@dappnode/dappmanager": "^0.1.0", - "@dappnode/types": "^0.1.25", + "@dappnode/types": "^0.1.26", "@reduxjs/toolkit": "^1.3.5", "@types/clipboard": "^2.0.7", "@types/node": "^18.11.18", diff --git a/packages/common/package.json b/packages/common/package.json index 36b525d1e..02bbd4c58 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -19,7 +19,7 @@ "lint": "eslint . --ext .ts --fix src" }, "dependencies": { - "@dappnode/types": "^0.1.25", + "@dappnode/types": "^0.1.26", "@types/node": "^18.11.18", "lodash-es": "^4.17.21" }, diff --git a/packages/dappmanager/package.json b/packages/dappmanager/package.json index ca62407df..ae543d3a1 100644 --- a/packages/dappmanager/package.json +++ b/packages/dappmanager/package.json @@ -24,7 +24,7 @@ "@dappnode/common": "^0.1.0", "@dappnode/schemas": "^0.1.7", "@dappnode/toolkit": "^0.1.19", - "@dappnode/types": "^0.1.25", + "@dappnode/types": "^0.1.26", "@ipld/car": "^3.1.16", "@types/async": "^3.0.1", "@types/async-retry": "^1.4.2", diff --git a/yarn.lock b/yarn.lock index 9252bb4b2..cf54f19bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1344,10 +1344,10 @@ resolved "https://registry.yarnpkg.com/@dappnode/types/-/types-0.1.23.tgz#e349661f40402b31fa3cd8651f16aed0ca927a79" integrity sha512-24O6fVfKqQHuHOgaNHvA7sbEZzYjCWxyIpFXaxnJxl4da6NRAEd+wZgzNzRQsqC5lLIQkNEc4uS+V+utgSGJuw== -"@dappnode/types@^0.1.25": - version "0.1.25" - resolved "https://registry.yarnpkg.com/@dappnode/types/-/types-0.1.25.tgz#b9b08abfbf839e2c03b3407064d10cf52c7a2d6e" - integrity sha512-+5gdP5IAtv7hpesZWmHDFGJlH39ZmAfhm/Yi+oXJ7hSpVPJJCWjsm5uRPtZ5TVXemEhKO2qwn9mhXOs71yDqWw== +"@dappnode/types@^0.1.26": + version "0.1.26" + resolved "https://registry.yarnpkg.com/@dappnode/types/-/types-0.1.26.tgz#c75fc25cb831f2026a5f9f928f51ba6f71451ce1" + integrity sha512-SzKAXzXrdEDy8SEoy2e7aENH/kobzW4HM749823pQ4Dgrht2tH4Rg3nBWYz3w37uzj6hAj+mIcAKMTe6yMEuig== "@emotion/is-prop-valid@^0.8.1": version "0.8.8" From 75a2a82de2aebf9dd5c70a6fbd57741565e8b608 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Mon, 11 Sep 2023 12:55:02 +0200 Subject: [PATCH 04/45] add basic calls --- .../admin-ui/src/__mock-backend__/index.ts | 10 +++++++- packages/common/src/routes.ts | 20 ++++++++++++++++ packages/common/src/types.ts | 20 ++++++++++++++++ packages/dappmanager/src/calls/index.ts | 5 ++++ packages/dappmanager/src/calls/optimism.ts | 24 +++++++++++++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/dappmanager/src/calls/optimism.ts diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index ed55e15dc..8c126188d 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -252,7 +252,15 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = { mail: "@example.com", isEnabled: true }), - disableEthicalMetrics: async () => {} + disableEthicalMetrics: async () => {}, + optimismConfigGet: async () => ({ + mainnetRpcUrl: "https://mainnet.optimism.io", + opHistoricalErigon: false, + opHistoricalGeth: false, + currentOpExecutionClient: "op-geth.dnp.dappnode.eth" + }), + optimismConfigSet: async () => {}, + optimismDisable: async () => {} }; export const calls: Routes = { diff --git a/packages/common/src/routes.ts b/packages/common/src/routes.ts index 169155c9a..9d7cb220e 100644 --- a/packages/common/src/routes.ts +++ b/packages/common/src/routes.ts @@ -44,6 +44,8 @@ import { StakerConfigGet, Eth2ClientTarget, EthicalMetricsConfig, + OptimismConfigSet, + OptimismConfigGet, } from "./types"; import { Network, PackageBackup, PackageEnvs } from "@dappnode/types"; @@ -386,6 +388,21 @@ export interface Routes { notification?: PackageNotification; }) => Promise<void>; + /** + * Enables Optimism with the given config + */ + optimismConfigSet: (kwargs: OptimismConfigSet) => Promise<void>; + + /** + * Returns the current Optimism configuration + */ + optimismConfigGet: () => Promise<OptimismConfigGet>; + + /** + * Disables Optimism + */ + optimismDisable: () => Promise<void>; + /** * Installs a DAppNode Package. * Resolves dependencies, downloads release assets, loads the images to docker, @@ -737,6 +754,9 @@ export const routesData: { [P in keyof Routes]: RouteData } = { notificationsGet: {}, notificationsRemove: {}, notificationsTest: {}, + optimismConfigGet: {}, + optimismConfigSet: { log: true }, + optimismDisable: {}, packageInstall: { log: true }, packageGet: {}, packagesGet: {}, diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 5f5af990a..71cdb7749 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -22,6 +22,7 @@ import { SignerPrater, SignerGnosis, SignerLukso, + ExecutionClientOptimism, } from "@dappnode/types"; /** @@ -1333,6 +1334,25 @@ export interface StakerCompatibleVersionsByNetwork<T extends Network> { compatibleMevBoost: { dnpName: MevBoost<T>; minVersion: string }; } +/** + * ======= + * ROLLUPS + * ======= + */ + +export interface OptimismConfigGet { + mainnetRpcUrl: string; + opHistoricalGeth: boolean | null; + opHistoricalErigon: boolean | null; + currentOpExecutionClient: ExecutionClientOptimism; +} + +export interface OptimismConfigSet { + mainnetRpcUrl: string; + enableHistorical: boolean; + targetOpExecutionClient: ExecutionClientOptimism; +} + /** * ======= * HANDLER diff --git a/packages/dappmanager/src/calls/index.ts b/packages/dappmanager/src/calls/index.ts index 55b77c9b1..f55a60c68 100644 --- a/packages/dappmanager/src/calls/index.ts +++ b/packages/dappmanager/src/calls/index.ts @@ -35,6 +35,11 @@ export { newFeatureStatusSet } from "./newFeatureStatusSet.js"; export { notificationsGet } from "./notificationsGet.js"; export { notificationsRemove } from "./notificationsRemove.js"; export { notificationsTest } from "./notificationsTest.js"; +export { + optimismConfigSet, + optimismConfigGet, + optimismDisable +} from "./optimism.js"; export { packageGet } from "./packageGet.js"; export { packagesGet } from "./packagesGet.js"; export { packageInstall } from "./packageInstall.js"; diff --git a/packages/dappmanager/src/calls/optimism.ts b/packages/dappmanager/src/calls/optimism.ts new file mode 100644 index 000000000..ad6538a40 --- /dev/null +++ b/packages/dappmanager/src/calls/optimism.ts @@ -0,0 +1,24 @@ +import { OptimismConfigGet, OptimismConfigSet } from "@dappnode/common"; + +/** + * Enables Optimism with the given configuration + * + * @param mainnetRpcUrl this is the RPC url of the mainnet node that will be used to connect to the Optimism network + * @param enableHistorical this enables the historical transactions on the Optimism network + * @param targetOpExecutionClient this is the client that will be used to connect to the Optimism network + */ +export async function optimismConfigSet({ + mainnetRpcUrl, + enableHistorical, + targetOpExecutionClient +}: OptimismConfigSet): Promise<void> {} + +/** + * Returns the current Optimism configuration + */ +export async function optimismConfigGet(): Promise<OptimismConfigGet> {} + +/** + * Disables Optimism by stopping the packages envolved + */ +export async function optimismDisable(): Promise<void> {} From bf33b615fb4e9d15746906ceeacb732d99d0a486 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Mon, 11 Sep 2023 15:58:39 +0200 Subject: [PATCH 05/45] stop progress --- packages/common/src/types.ts | 4 +-- packages/dappmanager/src/calls/optimism.ts | 31 +++++++++++++++++++++- packages/dappmanager/src/db/optimism.ts | 7 +++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 71cdb7749..296e26797 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1341,10 +1341,10 @@ export interface StakerCompatibleVersionsByNetwork<T extends Network> { */ export interface OptimismConfigGet { - mainnetRpcUrl: string; + mainnetRpcUrl: string | null; opHistoricalGeth: boolean | null; opHistoricalErigon: boolean | null; - currentOpExecutionClient: ExecutionClientOptimism; + currentOpExecutionClient: ExecutionClientOptimism | null; } export interface OptimismConfigSet { diff --git a/packages/dappmanager/src/calls/optimism.ts b/packages/dappmanager/src/calls/optimism.ts index ad6538a40..e42642ec8 100644 --- a/packages/dappmanager/src/calls/optimism.ts +++ b/packages/dappmanager/src/calls/optimism.ts @@ -1,4 +1,11 @@ import { OptimismConfigGet, OptimismConfigSet } from "@dappnode/common"; +import * as db from "../db/index.js"; +import { listPackageNoThrow } from "../modules/docker/list/listPackages.js"; +import { optimismNode } from "@dappnode/types"; +import { ComposeFileEditor } from "../modules/compose/editor.js"; + +const rpcUrlEnvName = "L1_RPC"; +const serviceName = "op-node"; /** * Enables Optimism with the given configuration @@ -16,7 +23,29 @@ export async function optimismConfigSet({ /** * Returns the current Optimism configuration */ -export async function optimismConfigGet(): Promise<OptimismConfigGet> {} +export async function optimismConfigGet(): Promise<OptimismConfigGet> { + let mainnetRpcUrl = null; + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + + if (opNodePackage) { + // get rpc url from environment variable + + const userSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + mainnetRpcUrl = userSettings.environment + ? userSettings.environment[serviceName][rpcUrlEnvName] + : null; + } + + return { + mainnetRpcUrl, + opHistoricalGeth: db.opHistoricalGeth.get(), + opHistoricalErigon: db.opHistoricalErigon.get(), + currentOpExecutionClient: db.opExecutionClient.get() + }; +} /** * Disables Optimism by stopping the packages envolved diff --git a/packages/dappmanager/src/db/optimism.ts b/packages/dappmanager/src/db/optimism.ts index fa974aeeb..b2ad0877c 100644 --- a/packages/dappmanager/src/db/optimism.ts +++ b/packages/dappmanager/src/db/optimism.ts @@ -1,3 +1,4 @@ +import { ExecutionClientOptimism } from "@dappnode/types"; import { dbMain } from "./dbFactory.js"; const OP_HISTORICAL_GETH = "op-historical-geth"; @@ -14,7 +15,5 @@ export const opHistoricalErigon = dbMain.staticKey<boolean>( false ); -export const opExecutionClient = dbMain.staticKey<string | null>( - OP_EXECUTION_CLIENT, - null -); +export const opExecutionClient = + dbMain.staticKey<ExecutionClientOptimism | null>(OP_EXECUTION_CLIENT, null); From 7b5433478d548255d17d7b79ef1af064f404a586 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 10:37:50 +0200 Subject: [PATCH 06/45] add optimism calls --- .../admin-ui/src/__mock-backend__/index.ts | 2 - packages/common/src/types.ts | 2 - packages/dappmanager/src/calls/optimism.ts | 174 +++++++++++++++++- packages/dappmanager/src/db/optimism.ts | 21 +-- 4 files changed, 174 insertions(+), 25 deletions(-) diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index 8c126188d..a951331d7 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -255,8 +255,6 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = { disableEthicalMetrics: async () => {}, optimismConfigGet: async () => ({ mainnetRpcUrl: "https://mainnet.optimism.io", - opHistoricalErigon: false, - opHistoricalGeth: false, currentOpExecutionClient: "op-geth.dnp.dappnode.eth" }), optimismConfigSet: async () => {}, diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 296e26797..da4687783 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1342,8 +1342,6 @@ export interface StakerCompatibleVersionsByNetwork<T extends Network> { export interface OptimismConfigGet { mainnetRpcUrl: string | null; - opHistoricalGeth: boolean | null; - opHistoricalErigon: boolean | null; currentOpExecutionClient: ExecutionClientOptimism | null; } diff --git a/packages/dappmanager/src/calls/optimism.ts b/packages/dappmanager/src/calls/optimism.ts index e42642ec8..cac499922 100644 --- a/packages/dappmanager/src/calls/optimism.ts +++ b/packages/dappmanager/src/calls/optimism.ts @@ -1,14 +1,35 @@ -import { OptimismConfigGet, OptimismConfigSet } from "@dappnode/common"; +import { + OptimismConfigGet, + OptimismConfigSet, + UserSettings +} from "@dappnode/common"; import * as db from "../db/index.js"; import { listPackageNoThrow } from "../modules/docker/list/listPackages.js"; -import { optimismNode } from "@dappnode/types"; +import { + optimismNode, + optimismL2Geth, + executionClientsOptimism +} from "@dappnode/types"; import { ComposeFileEditor } from "../modules/compose/editor.js"; +import { packageInstall } from "./packageInstall.js"; +import { + dockerContainerStart, + dockerContainerStop +} from "../modules/docker/index.js"; +import { packageSetEnvironment } from "./packageSetEnvironment.js"; +import { packageRestartVolumes } from "./packageRestartVolumes.js"; -const rpcUrlEnvName = "L1_RPC"; -const serviceName = "op-node"; +const opNodeRpcUrlEnvName = "L1_RPC"; +const opNodeServiceName = "op-node"; /** - * Enables Optimism with the given configuration + * Enables Optimism with the given configuration: + * + * - Set in db the envs + * - Make sure install packages with userSettings: mainnetRpcUrl, enableHistorical, targetOpExecutionClient + * - Make sure packages are running: op-node, op-geth || op-erigon, and optionally l2geth + * - If there is a switch in the targetOpExecitonClient, and the enableHistorical has changed, then + * the volumes of the packages should be removed * * @param mainnetRpcUrl this is the RPC url of the mainnet node that will be used to connect to the Optimism network * @param enableHistorical this enables the historical transactions on the Optimism network @@ -18,7 +39,110 @@ export async function optimismConfigSet({ mainnetRpcUrl, enableHistorical, targetOpExecutionClient -}: OptimismConfigSet): Promise<void> {} +}: OptimismConfigSet): Promise<void> { + // Set new target in db. Must go before op-node package install + await db.opExecutionClient.set(targetOpExecutionClient); + + // op-node + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + if (!opNodePackage) { + const userSettings: UserSettings = { + environment: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: mainnetRpcUrl + } + } + }; + // make sure op-node is installed + await packageInstall({ + name: optimismNode, + userSettings: { [optimismNode]: userSettings } + }); + } else { + // Make sure package running + for (const container of opNodePackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + + // check if the current env is the same as the new one + const opNodeUserSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + + if ( + opNodeUserSettings.environment?.[opNodeServiceName]?.[ + opNodeRpcUrlEnvName + ] !== mainnetRpcUrl + ) { + await packageSetEnvironment({ + dnpName: optimismNode, + environmentByService: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: mainnetRpcUrl + } + } + }); + } + } + + const previousHistorical = db.opEnableHistoricalRpc.get(); + await db.opEnableHistoricalRpc.set(enableHistorical); + + // op Execution clients: op-geth || op-erigon + const targetOpExecutionClientPackage = await listPackageNoThrow({ + dnpName: targetOpExecutionClient + }); + if (!targetOpExecutionClientPackage) { + // make sure target package is installed + await packageInstall({ name: targetOpExecutionClient }); + } else { + // Remove previous volumes if historical is different + if (previousHistorical !== enableHistorical) + await packageRestartVolumes({ dnpName: targetOpExecutionClient }); + // Make sure target package is running + for (const container of targetOpExecutionClientPackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + } + // stop previous op execution clients + const previousOpExecutionClients = executionClientsOptimism.filter( + client => client !== targetOpExecutionClient + ); + for (const executionClient of previousOpExecutionClients) { + const executionClientPackage = await listPackageNoThrow({ + dnpName: executionClient + }); + if (executionClientPackage) { + for (const container of executionClientPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } + + // l2geth; + const l2gethPackage = await listPackageNoThrow({ + dnpName: optimismL2Geth + }); + if (enableHistorical) { + // Install l2geth + if (!l2gethPackage) { + await packageInstall({ name: optimismL2Geth }); + } else { + // Make sure package running + for (const container of l2gethPackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + } + } else { + // Stop package + if (l2gethPackage) { + for (const container of l2gethPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } +} /** * Returns the current Optimism configuration @@ -35,14 +159,12 @@ export async function optimismConfigGet(): Promise<OptimismConfigGet> { false ).getUserSettings(); mainnetRpcUrl = userSettings.environment - ? userSettings.environment[serviceName][rpcUrlEnvName] + ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] : null; } return { mainnetRpcUrl, - opHistoricalGeth: db.opHistoricalGeth.get(), - opHistoricalErigon: db.opHistoricalErigon.get(), currentOpExecutionClient: db.opExecutionClient.get() }; } @@ -50,4 +172,36 @@ export async function optimismConfigGet(): Promise<OptimismConfigGet> { /** * Disables Optimism by stopping the packages envolved */ -export async function optimismDisable(): Promise<void> {} +export async function optimismDisable(): Promise<void> { + // op-node + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + if (opNodePackage) { + // Stop package + for (const container of opNodePackage.containers) + if (container.running) await dockerContainerStop(container.containerName); + } + + // op Execution clients: op-geth || op-erigon + const currentOpExecutionClient = db.opExecutionClient.get(); + if (currentOpExecutionClient) { + const currentOpExecutionClientPackage = await listPackageNoThrow({ + dnpName: currentOpExecutionClient + }); + if (currentOpExecutionClientPackage) { + // Stop package + for (const container of currentOpExecutionClientPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } + + // l2geth; + const l2gethPackage = await listPackageNoThrow({ + dnpName: optimismL2Geth + }); + if (l2gethPackage) { + // Stop package + for (const container of l2gethPackage.containers) + if (container.running) await dockerContainerStop(container.containerName); + } +} diff --git a/packages/dappmanager/src/db/optimism.ts b/packages/dappmanager/src/db/optimism.ts index b2ad0877c..7700b03b9 100644 --- a/packages/dappmanager/src/db/optimism.ts +++ b/packages/dappmanager/src/db/optimism.ts @@ -1,19 +1,18 @@ import { ExecutionClientOptimism } from "@dappnode/types"; import { dbMain } from "./dbFactory.js"; +import { interceptGlobalEnvOnSet } from "./interceptGlobalEnvOnSet.js"; -const OP_HISTORICAL_GETH = "op-historical-geth"; -const OP_HISTORICAL_ERIGON = "op-historical-erigon"; const OP_EXECUTION_CLIENT = "op-execution-client"; +const OP_ENABLE_HISTORICAL_RPC = "op-enable-historical-rpc"; -export const opHistoricalGeth = dbMain.staticKey<boolean>( - OP_HISTORICAL_GETH, - false +// Global env to be in the op-node package +export const opExecutionClient = interceptGlobalEnvOnSet( + dbMain.staticKey<ExecutionClientOptimism | null>(OP_EXECUTION_CLIENT, null), + Object.keys({ OP_EXECUTION_CLIENT })[0] ); -export const opHistoricalErigon = dbMain.staticKey<boolean>( - OP_HISTORICAL_ERIGON, - false +// Global env to be in the op-execution packages +export const opEnableHistoricalRpc = interceptGlobalEnvOnSet( + dbMain.staticKey<boolean>(OP_ENABLE_HISTORICAL_RPC, false), + Object.keys({ OP_ENABLE_HISTORICAL_RPC })[0] ); - -export const opExecutionClient = - dbMain.staticKey<ExecutionClientOptimism | null>(OP_EXECUTION_CLIENT, null); From 088083e28967db4b18605d255d58ae27b6775668 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 11:46:34 +0200 Subject: [PATCH 07/45] add optimism module --- .../admin-ui/src/__mock-backend__/index.ts | 4 +- packages/common/src/routes.ts | 6 - packages/common/src/types.ts | 1 + packages/dappmanager/src/calls/index.ts | 6 +- packages/dappmanager/src/calls/optimism.ts | 207 ------------------ .../dappmanager/src/calls/optimismConfig.ts | 37 ++++ .../src/modules/optimism/getOptimismConfig.ts | 29 +++ .../dappmanager/src/modules/optimism/index.ts | 2 + .../src/modules/optimism/params.ts | 4 + .../src/modules/optimism/setOptimismConfig.ts | 122 +++++++++++ 10 files changed, 198 insertions(+), 220 deletions(-) delete mode 100644 packages/dappmanager/src/calls/optimism.ts create mode 100644 packages/dappmanager/src/calls/optimismConfig.ts create mode 100644 packages/dappmanager/src/modules/optimism/getOptimismConfig.ts create mode 100644 packages/dappmanager/src/modules/optimism/index.ts create mode 100644 packages/dappmanager/src/modules/optimism/params.ts create mode 100644 packages/dappmanager/src/modules/optimism/setOptimismConfig.ts diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index a951331d7..bfc07bdb5 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -255,10 +255,10 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = { disableEthicalMetrics: async () => {}, optimismConfigGet: async () => ({ mainnetRpcUrl: "https://mainnet.optimism.io", + historicalEnabled: true, currentOpExecutionClient: "op-geth.dnp.dappnode.eth" }), - optimismConfigSet: async () => {}, - optimismDisable: async () => {} + optimismConfigSet: async () => {} }; export const calls: Routes = { diff --git a/packages/common/src/routes.ts b/packages/common/src/routes.ts index 9d7cb220e..000a283c7 100644 --- a/packages/common/src/routes.ts +++ b/packages/common/src/routes.ts @@ -398,11 +398,6 @@ export interface Routes { */ optimismConfigGet: () => Promise<OptimismConfigGet>; - /** - * Disables Optimism - */ - optimismDisable: () => Promise<void>; - /** * Installs a DAppNode Package. * Resolves dependencies, downloads release assets, loads the images to docker, @@ -756,7 +751,6 @@ export const routesData: { [P in keyof Routes]: RouteData } = { notificationsTest: {}, optimismConfigGet: {}, optimismConfigSet: { log: true }, - optimismDisable: {}, packageInstall: { log: true }, packageGet: {}, packagesGet: {}, diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index da4687783..8c0a66786 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1342,6 +1342,7 @@ export interface StakerCompatibleVersionsByNetwork<T extends Network> { export interface OptimismConfigGet { mainnetRpcUrl: string | null; + historicalEnabled: boolean; currentOpExecutionClient: ExecutionClientOptimism | null; } diff --git a/packages/dappmanager/src/calls/index.ts b/packages/dappmanager/src/calls/index.ts index f55a60c68..3db8d3b79 100644 --- a/packages/dappmanager/src/calls/index.ts +++ b/packages/dappmanager/src/calls/index.ts @@ -35,11 +35,7 @@ export { newFeatureStatusSet } from "./newFeatureStatusSet.js"; export { notificationsGet } from "./notificationsGet.js"; export { notificationsRemove } from "./notificationsRemove.js"; export { notificationsTest } from "./notificationsTest.js"; -export { - optimismConfigSet, - optimismConfigGet, - optimismDisable -} from "./optimism.js"; +export { optimismConfigSet, optimismConfigGet } from "./optimismConfig.js"; export { packageGet } from "./packageGet.js"; export { packagesGet } from "./packagesGet.js"; export { packageInstall } from "./packageInstall.js"; diff --git a/packages/dappmanager/src/calls/optimism.ts b/packages/dappmanager/src/calls/optimism.ts deleted file mode 100644 index cac499922..000000000 --- a/packages/dappmanager/src/calls/optimism.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { - OptimismConfigGet, - OptimismConfigSet, - UserSettings -} from "@dappnode/common"; -import * as db from "../db/index.js"; -import { listPackageNoThrow } from "../modules/docker/list/listPackages.js"; -import { - optimismNode, - optimismL2Geth, - executionClientsOptimism -} from "@dappnode/types"; -import { ComposeFileEditor } from "../modules/compose/editor.js"; -import { packageInstall } from "./packageInstall.js"; -import { - dockerContainerStart, - dockerContainerStop -} from "../modules/docker/index.js"; -import { packageSetEnvironment } from "./packageSetEnvironment.js"; -import { packageRestartVolumes } from "./packageRestartVolumes.js"; - -const opNodeRpcUrlEnvName = "L1_RPC"; -const opNodeServiceName = "op-node"; - -/** - * Enables Optimism with the given configuration: - * - * - Set in db the envs - * - Make sure install packages with userSettings: mainnetRpcUrl, enableHistorical, targetOpExecutionClient - * - Make sure packages are running: op-node, op-geth || op-erigon, and optionally l2geth - * - If there is a switch in the targetOpExecitonClient, and the enableHistorical has changed, then - * the volumes of the packages should be removed - * - * @param mainnetRpcUrl this is the RPC url of the mainnet node that will be used to connect to the Optimism network - * @param enableHistorical this enables the historical transactions on the Optimism network - * @param targetOpExecutionClient this is the client that will be used to connect to the Optimism network - */ -export async function optimismConfigSet({ - mainnetRpcUrl, - enableHistorical, - targetOpExecutionClient -}: OptimismConfigSet): Promise<void> { - // Set new target in db. Must go before op-node package install - await db.opExecutionClient.set(targetOpExecutionClient); - - // op-node - const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); - if (!opNodePackage) { - const userSettings: UserSettings = { - environment: { - [opNodeServiceName]: { - [opNodeRpcUrlEnvName]: mainnetRpcUrl - } - } - }; - // make sure op-node is installed - await packageInstall({ - name: optimismNode, - userSettings: { [optimismNode]: userSettings } - }); - } else { - // Make sure package running - for (const container of opNodePackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - - // check if the current env is the same as the new one - const opNodeUserSettings = new ComposeFileEditor( - optimismNode, - false - ).getUserSettings(); - - if ( - opNodeUserSettings.environment?.[opNodeServiceName]?.[ - opNodeRpcUrlEnvName - ] !== mainnetRpcUrl - ) { - await packageSetEnvironment({ - dnpName: optimismNode, - environmentByService: { - [opNodeServiceName]: { - [opNodeRpcUrlEnvName]: mainnetRpcUrl - } - } - }); - } - } - - const previousHistorical = db.opEnableHistoricalRpc.get(); - await db.opEnableHistoricalRpc.set(enableHistorical); - - // op Execution clients: op-geth || op-erigon - const targetOpExecutionClientPackage = await listPackageNoThrow({ - dnpName: targetOpExecutionClient - }); - if (!targetOpExecutionClientPackage) { - // make sure target package is installed - await packageInstall({ name: targetOpExecutionClient }); - } else { - // Remove previous volumes if historical is different - if (previousHistorical !== enableHistorical) - await packageRestartVolumes({ dnpName: targetOpExecutionClient }); - // Make sure target package is running - for (const container of targetOpExecutionClientPackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - } - // stop previous op execution clients - const previousOpExecutionClients = executionClientsOptimism.filter( - client => client !== targetOpExecutionClient - ); - for (const executionClient of previousOpExecutionClients) { - const executionClientPackage = await listPackageNoThrow({ - dnpName: executionClient - }); - if (executionClientPackage) { - for (const container of executionClientPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } - } - - // l2geth; - const l2gethPackage = await listPackageNoThrow({ - dnpName: optimismL2Geth - }); - if (enableHistorical) { - // Install l2geth - if (!l2gethPackage) { - await packageInstall({ name: optimismL2Geth }); - } else { - // Make sure package running - for (const container of l2gethPackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - } - } else { - // Stop package - if (l2gethPackage) { - for (const container of l2gethPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } - } -} - -/** - * Returns the current Optimism configuration - */ -export async function optimismConfigGet(): Promise<OptimismConfigGet> { - let mainnetRpcUrl = null; - const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); - - if (opNodePackage) { - // get rpc url from environment variable - - const userSettings = new ComposeFileEditor( - optimismNode, - false - ).getUserSettings(); - mainnetRpcUrl = userSettings.environment - ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] - : null; - } - - return { - mainnetRpcUrl, - currentOpExecutionClient: db.opExecutionClient.get() - }; -} - -/** - * Disables Optimism by stopping the packages envolved - */ -export async function optimismDisable(): Promise<void> { - // op-node - const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); - if (opNodePackage) { - // Stop package - for (const container of opNodePackage.containers) - if (container.running) await dockerContainerStop(container.containerName); - } - - // op Execution clients: op-geth || op-erigon - const currentOpExecutionClient = db.opExecutionClient.get(); - if (currentOpExecutionClient) { - const currentOpExecutionClientPackage = await listPackageNoThrow({ - dnpName: currentOpExecutionClient - }); - if (currentOpExecutionClientPackage) { - // Stop package - for (const container of currentOpExecutionClientPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } - } - - // l2geth; - const l2gethPackage = await listPackageNoThrow({ - dnpName: optimismL2Geth - }); - if (l2gethPackage) { - // Stop package - for (const container of l2gethPackage.containers) - if (container.running) await dockerContainerStop(container.containerName); - } -} diff --git a/packages/dappmanager/src/calls/optimismConfig.ts b/packages/dappmanager/src/calls/optimismConfig.ts new file mode 100644 index 000000000..7f94f65b5 --- /dev/null +++ b/packages/dappmanager/src/calls/optimismConfig.ts @@ -0,0 +1,37 @@ +import { OptimismConfigGet, OptimismConfigSet } from "@dappnode/common"; +import { + getOptimismConfig, + setOptimismConfig +} from "../modules/optimism/index.js"; + +/** + * Enables Optimism with the given configuration: + * + * - Set in db the envs + * - Make sure install packages with userSettings: mainnetRpcUrl, enableHistorical, targetOpExecutionClient + * - Make sure packages are running: op-node, op-geth || op-erigon, and optionally l2geth + * - If there is a switch in the targetOpExecitonClient, and the enableHistorical has changed, then + * the volumes of the packages should be removed + * + * @param mainnetRpcUrl this is the RPC url of the mainnet node that will be used to connect to the Optimism network + * @param enableHistorical this enables the historical transactions on the Optimism network + * @param targetOpExecutionClient this is the client that will be used to connect to the Optimism network + */ +export async function optimismConfigSet({ + mainnetRpcUrl, + enableHistorical, + targetOpExecutionClient +}: OptimismConfigSet): Promise<void> { + await setOptimismConfig({ + mainnetRpcUrl, + enableHistorical, + targetOpExecutionClient + }); +} + +/** + * Returns the current Optimism configuration + */ +export async function optimismConfigGet(): Promise<OptimismConfigGet> { + return await getOptimismConfig(); +} diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts new file mode 100644 index 000000000..5f59e5404 --- /dev/null +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -0,0 +1,29 @@ +import { OptimismConfigGet } from "@dappnode/common"; +import * as db from "../../db/index.js"; +import { listPackageNoThrow } from "../docker/list/listPackages.js"; +import { optimismNode } from "@dappnode/types"; +import { ComposeFileEditor } from "../compose/editor.js"; +import { opNodeRpcUrlEnvName, opNodeServiceName } from "./params.js"; + +export async function getOptimismConfig(): Promise<OptimismConfigGet> { + let mainnetRpcUrl = null; + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + + if (opNodePackage) { + // get rpc url from environment variable + + const userSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + mainnetRpcUrl = userSettings.environment + ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] + : null; + } + + return { + mainnetRpcUrl, + historicalEnabled: db.opEnableHistoricalRpc.get(), + currentOpExecutionClient: db.opExecutionClient.get() + }; +} diff --git a/packages/dappmanager/src/modules/optimism/index.ts b/packages/dappmanager/src/modules/optimism/index.ts new file mode 100644 index 000000000..6b6b1e545 --- /dev/null +++ b/packages/dappmanager/src/modules/optimism/index.ts @@ -0,0 +1,2 @@ +export { getOptimismConfig } from "./getOptimismConfig.js"; +export { setOptimismConfig } from "./setOptimismConfig.js"; diff --git a/packages/dappmanager/src/modules/optimism/params.ts b/packages/dappmanager/src/modules/optimism/params.ts new file mode 100644 index 000000000..78e118002 --- /dev/null +++ b/packages/dappmanager/src/modules/optimism/params.ts @@ -0,0 +1,4 @@ + + +export const opNodeRpcUrlEnvName = "L1_RPC"; +export const opNodeServiceName = "op-node"; \ No newline at end of file diff --git a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts new file mode 100644 index 000000000..b94325908 --- /dev/null +++ b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts @@ -0,0 +1,122 @@ +import { OptimismConfigSet, UserSettings } from "@dappnode/common"; +import * as db from "../../db/index.js"; +import { listPackageNoThrow } from "../docker/list/listPackages.js"; +import { + optimismNode, + optimismL2Geth, + executionClientsOptimism +} from "@dappnode/types"; +import { ComposeFileEditor } from "../compose/editor.js"; +import { packageInstall } from "../../calls/packageInstall.js"; +import { dockerContainerStart, dockerContainerStop } from "../docker/index.js"; +import { packageSetEnvironment } from "../../calls/packageSetEnvironment.js"; +import { opNodeServiceName, opNodeRpcUrlEnvName } from "./params.js"; + +export async function setOptimismConfig({ + mainnetRpcUrl, + enableHistorical, + targetOpExecutionClient +}: OptimismConfigSet): Promise<void> { + // Set new target in db. Must go before op-node package install + await db.opExecutionClient.set(targetOpExecutionClient); + + // op-node + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + if (!opNodePackage) { + const userSettings: UserSettings = { + environment: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: mainnetRpcUrl + } + } + }; + // make sure op-node is installed + await packageInstall({ + name: optimismNode, + userSettings: { [optimismNode]: userSettings } + }); + } else { + // Make sure package running + for (const container of opNodePackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + + // check if the current env is the same as the new one + const opNodeUserSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + + if ( + opNodeUserSettings.environment?.[opNodeServiceName]?.[ + opNodeRpcUrlEnvName + ] !== mainnetRpcUrl + ) { + await packageSetEnvironment({ + dnpName: optimismNode, + environmentByService: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: mainnetRpcUrl + } + } + }); + } + } + + //const previousHistorical = db.opEnableHistoricalRpc.get(); + await db.opEnableHistoricalRpc.set(enableHistorical); + + // op Execution clients: op-geth || op-erigon + const targetOpExecutionClientPackage = await listPackageNoThrow({ + dnpName: targetOpExecutionClient + }); + if (!targetOpExecutionClientPackage) { + // make sure target package is installed + await packageInstall({ name: targetOpExecutionClient }); + } else { + // TODO: Remove previous volumes if historical is different (danger removing volumes is too intrusives) + /**if (previousHistorical !== enableHistorical) + await packageRestartVolumes({ dnpName: targetOpExecutionClient });*/ + // Make sure target package is running + for (const container of targetOpExecutionClientPackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + } + // stop previous op execution clients + const previousOpExecutionClients = executionClientsOptimism.filter( + client => client !== targetOpExecutionClient + ); + for (const executionClient of previousOpExecutionClients) { + const executionClientPackage = await listPackageNoThrow({ + dnpName: executionClient + }); + if (executionClientPackage) { + for (const container of executionClientPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } + + // l2geth; + const l2gethPackage = await listPackageNoThrow({ + dnpName: optimismL2Geth + }); + if (enableHistorical) { + // Install l2geth + if (!l2gethPackage) { + await packageInstall({ name: optimismL2Geth }); + } else { + // Make sure package running + for (const container of l2gethPackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + } + } else { + // Stop package + if (l2gethPackage) { + for (const container of l2gethPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } +} From 51bc7f9d77ec56d81af847b6cc5a6ac9e75ec565 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 13:33:27 +0200 Subject: [PATCH 08/45] implemented types and getOptimismConfig --- packages/common/src/types.ts | 61 ++++++-- .../dappmanager/src/calls/fetchDnpRequest.ts | 21 +++ .../src/daemons/stakerConfig/index.ts | 4 +- packages/dappmanager/src/db/stakerConfig.ts | 16 ++- .../src/modules/optimism/getOptimismConfig.ts | 130 +++++++++++++++++- .../src/modules/optimism/params.ts | 4 +- .../stakerConfig/get/getStakerConfig.ts | 48 +------ .../src/modules/stakerConfig/utils.ts | 33 +---- .../dappmanager/src/utils/getPkgItemData.ts | 53 +++++++ 9 files changed, 270 insertions(+), 100 deletions(-) create mode 100644 packages/dappmanager/src/utils/getPkgItemData.ts diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 8c0a66786..cad574ab4 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -23,6 +23,8 @@ import { SignerGnosis, SignerLukso, ExecutionClientOptimism, + OptimismL2Geth, + OptimismNode, } from "@dappnode/types"; /** @@ -1230,7 +1232,7 @@ export type StakerItemError<T extends Network, P extends StakerType> = { /** * Metadata of a staker item to be cached */ -export type StakerItemData = Pick< +export type PackageItemData = Pick< PackageRelease, | "dnpName" | "reqVersion" @@ -1249,7 +1251,7 @@ export type StakerItemOk<T extends Network, P extends StakerType> = { isInstalled: boolean; isUpdated: boolean; isRunning: boolean; - data?: StakerItemData; + data?: PackageItemData; isSelected: boolean; } & StakerItemBasic<T, P>; @@ -1340,16 +1342,59 @@ export interface StakerCompatibleVersionsByNetwork<T extends Network> { * ======= */ +export type OptimismType = "archive" | "execution" | "rollup"; +export type OptimismItem<T extends OptimismType> = + | OptimismItemOk<T> + | OptimismItemError<T>; +interface OptimismArchive { + dnpName: OptimismL2Geth; +} +interface OptimismExecution { + dnpName: ExecutionClientOptimism; + enableHistorical: boolean; +} +interface OptimismRollup { + dnpName: OptimismNode; + mainnetRpcUrl: string; +} +type OptimismItemBasic<T extends OptimismType> = T extends "archive" + ? OptimismArchive + : T extends "execution" + ? OptimismExecution + : T extends "rollup" + ? OptimismRollup + : never; + +export type OptimismItemError<T extends OptimismType> = { + status: "error"; + error: string; +} & OptimismItemBasic<T>; +export type OptimismItemOk<T extends OptimismType> = { + status: "ok"; + avatarUrl: string; + isInstalled: boolean; + isUpdated: boolean; + isRunning: boolean; + data?: PackageItemData; + isSelected: boolean; +} & OptimismItemBasic<T>; + export interface OptimismConfigGet { - mainnetRpcUrl: string | null; - historicalEnabled: boolean; - currentOpExecutionClient: ExecutionClientOptimism | null; + archive: OptimismItem<"archive">; + executionClients: OptimismItem<"execution">[]; + rollup: OptimismItem<"rollup">; +} + +export interface OptimismConfigGetOk { + archive: OptimismItemOk<"archive">; + executionClients: OptimismItemOk<"execution">[]; + rollup: OptimismItemOk<"rollup">; } export interface OptimismConfigSet { - mainnetRpcUrl: string; - enableHistorical: boolean; - targetOpExecutionClient: ExecutionClientOptimism; + archive?: OptimismItemOk<"archive">; + executionClient?: OptimismItemOk<"execution">; + rollup?: OptimismItemOk<"rollup">; } /** diff --git a/packages/dappmanager/src/calls/fetchDnpRequest.ts b/packages/dappmanager/src/calls/fetchDnpRequest.ts index f5d533552..ee766cbee 100644 --- a/packages/dappmanager/src/calls/fetchDnpRequest.ts +++ b/packages/dappmanager/src/calls/fetchDnpRequest.ts @@ -153,6 +153,27 @@ export function getIsUpdated( return !shouldUpdate(dnp.version, reqVersion); } +/** + * Returns true if the package is running or false if not + * For web3signer, it does not take into account the container "flyway" which may not be running + */ +export function getIsRunning( + { dnpName }: { dnpName: string }, + dnpList: InstalledPackageData[] +): boolean { + const flywayServiceName = "flyway"; + const isSigner = dnpName.includes("web3signer"); + const dnp = dnpList.find(dnp => dnp.dnpName === dnpName); + if (dnp) { + if (isSigner) + return dnp.containers + .filter(c => c.serviceName !== flywayServiceName) + .every(c => c.running); + else return dnp.containers.every(c => c.running); + } + return false; +} + function getRequiresCoreUpdate( { metadata }: { metadata: Manifest }, dnpList: InstalledPackageData[] diff --git a/packages/dappmanager/src/daemons/stakerConfig/index.ts b/packages/dappmanager/src/daemons/stakerConfig/index.ts index 7684edbb9..a5780294e 100644 --- a/packages/dappmanager/src/daemons/stakerConfig/index.ts +++ b/packages/dappmanager/src/daemons/stakerConfig/index.ts @@ -1,9 +1,9 @@ import { eventBus } from "../../eventBus.js"; import * as db from "../../db/index.js"; import { logs } from "../../logs.js"; -import { pickStakerItemData } from "../../modules/stakerConfig/utils.js"; import { ReleaseFetcher } from "../../modules/release/index.js"; import { memoizeDebounce } from "../../utils/asyncFlows.js"; +import { pickPackageItemData } from "../../utils/getPkgItemData.js"; async function runStakerCacheUpdate({ dnpName @@ -13,7 +13,7 @@ async function runStakerCacheUpdate({ try { const releaseFetcher = new ReleaseFetcher(); const repository = await releaseFetcher.getRelease(dnpName); - const dataDnp = pickStakerItemData(repository); + const dataDnp = pickPackageItemData(repository); db.stakerItemMetadata.set(dnpName, dataDnp); } catch (e) { logs.error("Error on staker cache update daemon", e); diff --git a/packages/dappmanager/src/db/stakerConfig.ts b/packages/dappmanager/src/db/stakerConfig.ts index fc4007ee5..df321a676 100644 --- a/packages/dappmanager/src/db/stakerConfig.ts +++ b/packages/dappmanager/src/db/stakerConfig.ts @@ -1,4 +1,4 @@ -import { StakerItemData } from "@dappnode/common"; +import { PackageItemData } from "@dappnode/common"; import { dbCache, dbMain } from "./dbFactory.js"; import { interceptGlobalEnvOnSet } from "./interceptGlobalEnvOnSet.js"; import { @@ -16,12 +16,14 @@ import { const STAKER_ITEM_METADATA = "staker-item-metadata"; -export const stakerItemMetadata = dbCache.indexedByKey<StakerItemData, string>({ - rootKey: STAKER_ITEM_METADATA, - getKey: target => target, - validate: (id, metadata) => - typeof id === "string" && typeof metadata === "object" -}); +export const stakerItemMetadata = dbCache.indexedByKey<PackageItemData, string>( + { + rootKey: STAKER_ITEM_METADATA, + getKey: target => target, + validate: (id, metadata) => + typeof id === "string" && typeof metadata === "object" + } +); // Mainnet diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index 5f59e5404..5887f1084 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -1,12 +1,124 @@ -import { OptimismConfigGet } from "@dappnode/common"; +import { OptimismConfigGet, OptimismItem } from "@dappnode/common"; import * as db from "../../db/index.js"; -import { listPackageNoThrow } from "../docker/list/listPackages.js"; -import { optimismNode } from "@dappnode/types"; +import { listPackages } from "../docker/list/listPackages.js"; +import { + executionClientsOptimism, + optimismL2Geth, + optimismNode +} from "@dappnode/types"; import { ComposeFileEditor } from "../compose/editor.js"; import { opNodeRpcUrlEnvName, opNodeServiceName } from "./params.js"; +import { ReleaseFetcher } from "../release/index.js"; +import { getPkgData } from "../../utils/getPkgItemData.js"; +import { + getIsInstalled, + getIsRunning, + getIsUpdated +} from "../../calls/fetchDnpRequest.js"; +import { fileToGatewayUrl } from "../../utils/distributedFile.js"; export async function getOptimismConfig(): Promise<OptimismConfigGet> { - let mainnetRpcUrl = null; + try { + const releaseFetcher = new ReleaseFetcher(); + + const currentOptimismExecutionClient = db.opExecutionClient.get(); + const enableHistorical = db.opEnableHistoricalRpc.get(); + const mainnetRpcUrl = getOptimismNodeRpcUrl(); + const dnpList = await listPackages(); + + return { + executionClients: await Promise.all( + executionClientsOptimism.map(async execClient => { + try { + if (!(await releaseFetcher.repoExists(execClient))) + throw Error(`Repository ${execClient} does not exist`); + + const pkgData = await getPkgData(releaseFetcher, execClient); + + return { + status: "ok", + dnpName: execClient, + avatarUrl: fileToGatewayUrl(pkgData.avatarFile), + isInstalled: getIsInstalled(pkgData, dnpList), + isUpdated: getIsUpdated(pkgData, dnpList), + isRunning: getIsRunning(pkgData, dnpList), + data: pkgData, + isSelected: execClient === currentOptimismExecutionClient, + enableHistorical + }; + } catch (error) { + return { + status: "error", + dnpName: execClient, + error, + enableHistorical + }; + } + }) + ), + rollup: await new Promise<OptimismItem<"rollup">>(resolve => { + (async () => { + try { + if (!(await releaseFetcher.repoExists(optimismNode))) + throw Error(`Repository ${optimismNode} does not exist`); + const pkgData = await getPkgData(releaseFetcher, optimismNode); + const isRunning = getIsRunning(pkgData, dnpList); + resolve({ + status: "ok", + dnpName: optimismNode, + avatarUrl: fileToGatewayUrl(pkgData.avatarFile), + isInstalled: getIsInstalled(pkgData, dnpList), + isUpdated: getIsUpdated(pkgData, dnpList), + isRunning, + data: pkgData, + isSelected: isRunning, + mainnetRpcUrl + }); + } catch (error) { + resolve({ + status: "error", + dnpName: optimismNode, + error, + mainnetRpcUrl + }); + } + })(); + }), + + archive: await new Promise<OptimismItem<"archive">>(resolve => { + (async () => { + try { + if (!(await releaseFetcher.repoExists(optimismL2Geth))) + throw Error(`Repository ${optimismL2Geth} does not exist`); + const pkgData = await getPkgData(releaseFetcher, optimismL2Geth); + const isRunning = getIsRunning(pkgData, dnpList); + resolve({ + status: "ok", + dnpName: optimismL2Geth, + avatarUrl: fileToGatewayUrl(pkgData.avatarFile), + isInstalled: getIsInstalled(pkgData, dnpList), + isUpdated: getIsUpdated(pkgData, dnpList), + isRunning, + data: pkgData, + isSelected: isRunning && enableHistorical + }); + } catch (error) { + resolve({ + status: "error", + dnpName: optimismL2Geth, + error + }); + } + })(); + }) + }; + } catch (e) { + throw Error(`Error getting Optimism config: ${e}`); + } +} + +/** + * let mainnetRpcUrl = null; const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); if (opNodePackage) { @@ -26,4 +138,14 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { historicalEnabled: db.opEnableHistoricalRpc.get(), currentOpExecutionClient: db.opExecutionClient.get() }; + */ + +function getOptimismNodeRpcUrl(): string { + const userSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + return userSettings.environment + ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] + : ""; } diff --git a/packages/dappmanager/src/modules/optimism/params.ts b/packages/dappmanager/src/modules/optimism/params.ts index 78e118002..1579df4d0 100644 --- a/packages/dappmanager/src/modules/optimism/params.ts +++ b/packages/dappmanager/src/modules/optimism/params.ts @@ -1,4 +1,2 @@ - - export const opNodeRpcUrlEnvName = "L1_RPC"; -export const opNodeServiceName = "op-node"; \ No newline at end of file +export const opNodeServiceName = "op-node"; diff --git a/packages/dappmanager/src/modules/stakerConfig/get/getStakerConfig.ts b/packages/dappmanager/src/modules/stakerConfig/get/getStakerConfig.ts index 359a1b2e4..fafa50b64 100644 --- a/packages/dappmanager/src/modules/stakerConfig/get/getStakerConfig.ts +++ b/packages/dappmanager/src/modules/stakerConfig/get/getStakerConfig.ts @@ -1,27 +1,25 @@ import { packageGet } from "../../../calls/index.js"; import { getIsInstalled, + getIsRunning, getIsUpdated } from "../../../calls/fetchDnpRequest.js"; import { ConsensusClient, ExecutionClient, - InstalledPackageData, MevBoost, Signer, StakerConfigGet, - StakerItem, - StakerItemData + StakerItem } from "@dappnode/common"; import { fileToGatewayUrl } from "../../../utils/distributedFile.js"; import { listPackages } from "../../docker/list/index.js"; import { ReleaseFetcher } from "../../release/index.js"; -import { getBeaconServiceName, pickStakerItemData } from "../utils.js"; +import { getBeaconServiceName } from "../utils.js"; import { Network } from "@dappnode/types"; import { getStakerDnpNamesByNetwork } from "./getStakerDnpNamesByNetwork.js"; import { getStakerConfigByNetwork } from "../index.js"; -import { eventBus } from "../../../eventBus.js"; -import * as db from "../../../db/index.js"; +import { getPkgData } from "../../../utils/getPkgItemData.js"; /** * Fetches the current staker configuration: @@ -188,41 +186,3 @@ export async function getStakerConfig<T extends Network>( throw Error(`Error getting staker config: ${e}`); } } - -/** - * Returns true if the package is running or false if not - * For web3signer, it does not take into account the container "flyway" which may not be running - */ -function getIsRunning( - { dnpName }: { dnpName: string }, - dnpList: InstalledPackageData[] -): boolean { - const flywayServiceName = "flyway"; - const isSigner = dnpName.includes("web3signer"); - const dnp = dnpList.find(dnp => dnp.dnpName === dnpName); - if (dnp) { - if (isSigner) - return dnp.containers - .filter(c => c.serviceName !== flywayServiceName) - .every(c => c.running); - else return dnp.containers.every(c => c.running); - } - return false; -} - -async function getPkgData( - releaseFetcher: ReleaseFetcher, - dnpName: string -): Promise<StakerItemData> { - const cachedDnp = db.stakerItemMetadata.get(dnpName); - if (cachedDnp) { - // Update cache in the background - eventBus.runStakerCacheUpdate.emit({ dnpName }); - return cachedDnp; - } else { - const repository = await releaseFetcher.getRelease(dnpName); - const dataDnp = pickStakerItemData(repository); - db.stakerItemMetadata.set(dnpName, dataDnp); - return dataDnp; - } -} diff --git a/packages/dappmanager/src/modules/stakerConfig/utils.ts b/packages/dappmanager/src/modules/stakerConfig/utils.ts index 0d951e8e8..082a82821 100644 --- a/packages/dappmanager/src/modules/stakerConfig/utils.ts +++ b/packages/dappmanager/src/modules/stakerConfig/utils.ts @@ -1,14 +1,11 @@ import { UserSettingsAllDnps, - StakerItemData, - PackageRelease, ConsensusClient, ExecutionClient, StakerConfigByNetwork } from "@dappnode/common"; -import { pick } from "lodash-es"; -import { Manifest, Network } from "@dappnode/types"; import * as db from "../../db/index.js"; +import { Network } from "@dappnode/types"; export function getStakerConfigByNetwork<T extends Network>( network: T @@ -132,31 +129,3 @@ const getDefaultCheckpointSync = (network: Network): string => : network === "lukso" ? "https://checkpoints.mainnet.lukso.network" : ""; - -export function pickStakerItemData(pkgRelease: PackageRelease): StakerItemData { - return { - metadata: pickStakerManifestData(pkgRelease.metadata), - ...pick(pkgRelease, [ - "dnpName", - "reqVersion", - "semVersion", - "imageFile", - "avatarFile", - "warnings", - "origin", - "signedSafe" - ] as const) - }; -} - -function pickStakerManifestData(manifest: Manifest): Manifest { - return pick(manifest, [ - "name", - "version", - "shortDescription", - "avatar", - "links", - "chain", - "warnings" - ] as const); -} diff --git a/packages/dappmanager/src/utils/getPkgItemData.ts b/packages/dappmanager/src/utils/getPkgItemData.ts new file mode 100644 index 000000000..5e68b8c8d --- /dev/null +++ b/packages/dappmanager/src/utils/getPkgItemData.ts @@ -0,0 +1,53 @@ +import { PackageItemData, PackageRelease } from "@dappnode/common"; +import { eventBus } from "../eventBus.js"; +import { ReleaseFetcher } from "../modules/release/index.js"; +import * as db from "../db/index.js"; +import { pick } from "lodash-es"; +import { Manifest } from "@dappnode/types"; + +export async function getPkgData( + releaseFetcher: ReleaseFetcher, + dnpName: string +): Promise<PackageItemData> { + const cachedDnp = db.stakerItemMetadata.get(dnpName); + if (cachedDnp) { + // Update cache in the background + eventBus.runStakerCacheUpdate.emit({ dnpName }); + return cachedDnp; + } else { + const repository = await releaseFetcher.getRelease(dnpName); + const dataDnp = pickPackageItemData(repository); + db.stakerItemMetadata.set(dnpName, dataDnp); + return dataDnp; + } +} + +export function pickPackageItemData( + pkgRelease: PackageRelease +): PackageItemData { + return { + metadata: pickPackageManifestData(pkgRelease.metadata), + ...pick(pkgRelease, [ + "dnpName", + "reqVersion", + "semVersion", + "imageFile", + "avatarFile", + "warnings", + "origin", + "signedSafe" + ] as const) + }; +} + +function pickPackageManifestData(manifest: Manifest): Manifest { + return pick(manifest, [ + "name", + "version", + "shortDescription", + "avatar", + "links", + "chain", + "warnings" + ] as const); +} From 0c3149d1b7cd7a1fa81eb58ba94ed481449f76a4 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 15:29:00 +0200 Subject: [PATCH 09/45] move getPkgData to utils --- .../src/daemons/stakerConfig/index.ts | 2 +- packages/dappmanager/src/db/index.ts | 2 +- .../src/db/{optimism.ts => optimismConfig.ts} | 17 ++++++++++++++++- .../dappmanager/src/utils/getPkgItemData.ts | 4 ++-- 4 files changed, 20 insertions(+), 5 deletions(-) rename packages/dappmanager/src/db/{optimism.ts => optimismConfig.ts} (62%) diff --git a/packages/dappmanager/src/daemons/stakerConfig/index.ts b/packages/dappmanager/src/daemons/stakerConfig/index.ts index a5780294e..7bc2260fb 100644 --- a/packages/dappmanager/src/daemons/stakerConfig/index.ts +++ b/packages/dappmanager/src/daemons/stakerConfig/index.ts @@ -14,7 +14,7 @@ async function runStakerCacheUpdate({ const releaseFetcher = new ReleaseFetcher(); const repository = await releaseFetcher.getRelease(dnpName); const dataDnp = pickPackageItemData(repository); - db.stakerItemMetadata.set(dnpName, dataDnp); + db.optimismItemMetadata.set(dnpName, dataDnp); } catch (e) { logs.error("Error on staker cache update daemon", e); } diff --git a/packages/dappmanager/src/db/index.ts b/packages/dappmanager/src/db/index.ts index c5e8d69a9..b1c626beb 100644 --- a/packages/dappmanager/src/db/index.ts +++ b/packages/dappmanager/src/db/index.ts @@ -8,7 +8,7 @@ export * from "./ipfsClient.js"; export * from "./fileTransferPath.js"; export * from "./network.js"; export * from "./notification.js"; -export * from "./optimism.js"; +export * from "./optimismConfig.js"; export * from "./package.js"; export * from "./registry.js"; export * from "./releaseKeys.js"; diff --git a/packages/dappmanager/src/db/optimism.ts b/packages/dappmanager/src/db/optimismConfig.ts similarity index 62% rename from packages/dappmanager/src/db/optimism.ts rename to packages/dappmanager/src/db/optimismConfig.ts index 7700b03b9..fa184a729 100644 --- a/packages/dappmanager/src/db/optimism.ts +++ b/packages/dappmanager/src/db/optimismConfig.ts @@ -1,10 +1,25 @@ import { ExecutionClientOptimism } from "@dappnode/types"; -import { dbMain } from "./dbFactory.js"; +import { dbCache, dbMain } from "./dbFactory.js"; import { interceptGlobalEnvOnSet } from "./interceptGlobalEnvOnSet.js"; +import { PackageItemData } from "@dappnode/common"; const OP_EXECUTION_CLIENT = "op-execution-client"; const OP_ENABLE_HISTORICAL_RPC = "op-enable-historical-rpc"; +// Cache + +const OPTIMISM_ITEM_METADATA = "optimism-item-metadata"; + +export const optimismItemMetadata = dbCache.indexedByKey< + PackageItemData, + string +>({ + rootKey: OPTIMISM_ITEM_METADATA, + getKey: target => target, + validate: (id, metadata) => + typeof id === "string" && typeof metadata === "object" +}); + // Global env to be in the op-node package export const opExecutionClient = interceptGlobalEnvOnSet( dbMain.staticKey<ExecutionClientOptimism | null>(OP_EXECUTION_CLIENT, null), diff --git a/packages/dappmanager/src/utils/getPkgItemData.ts b/packages/dappmanager/src/utils/getPkgItemData.ts index 5e68b8c8d..e4ee6cd2f 100644 --- a/packages/dappmanager/src/utils/getPkgItemData.ts +++ b/packages/dappmanager/src/utils/getPkgItemData.ts @@ -9,7 +9,7 @@ export async function getPkgData( releaseFetcher: ReleaseFetcher, dnpName: string ): Promise<PackageItemData> { - const cachedDnp = db.stakerItemMetadata.get(dnpName); + const cachedDnp = db.optimismItemMetadata.get(dnpName); if (cachedDnp) { // Update cache in the background eventBus.runStakerCacheUpdate.emit({ dnpName }); @@ -17,7 +17,7 @@ export async function getPkgData( } else { const repository = await releaseFetcher.getRelease(dnpName); const dataDnp = pickPackageItemData(repository); - db.stakerItemMetadata.set(dnpName, dataDnp); + db.optimismItemMetadata.set(dnpName, dataDnp); return dataDnp; } } From 47a012bc6b7e23c640ba27d0232efdf8ed8175ad Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 15:38:07 +0200 Subject: [PATCH 10/45] remove unused code --- .../src/modules/optimism/getOptimismConfig.ts | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index 5887f1084..b77950dbd 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -117,29 +117,6 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { } } -/** - * let mainnetRpcUrl = null; - const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); - - if (opNodePackage) { - // get rpc url from environment variable - - const userSettings = new ComposeFileEditor( - optimismNode, - false - ).getUserSettings(); - mainnetRpcUrl = userSettings.environment - ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] - : null; - } - - return { - mainnetRpcUrl, - historicalEnabled: db.opEnableHistoricalRpc.get(), - currentOpExecutionClient: db.opExecutionClient.get() - }; - */ - function getOptimismNodeRpcUrl(): string { const userSettings = new ComposeFileEditor( optimismNode, From bd7cebbf100659c30cb6471e8517c6c68bd436d6 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 16:20:29 +0200 Subject: [PATCH 11/45] add mock data --- .../admin-ui/src/__mock-backend__/index.ts | 116 +++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index bfc07bdb5..45163cb69 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -254,9 +254,119 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = { }), disableEthicalMetrics: async () => {}, optimismConfigGet: async () => ({ - mainnetRpcUrl: "https://mainnet.optimism.io", - historicalEnabled: true, - currentOpExecutionClient: "op-geth.dnp.dappnode.eth" + executionClients: [ + { + status: "ok", + dnpName: "op-geth.dnp.dappnode.eth", + avatarUrl: "", + isInstalled: true, + isUpdated: true, + isRunning: true, + data: { + dnpName: "package", + reqVersion: "0.1.0", + semVersion: "0.1.0", + imageFile: { + hash: "QM..", + source: "ipfs", + size: 123 + }, + warnings: {}, + signedSafe: true, + metadata: { + name: "geth.dnp.dappnode.eth", + description: "Go implementation of ethereum. Execution client", + shortDescription: "Go implementation of ethereum", + version: "0.1.0" + } + }, + isSelected: true, + enableHistorical: true + }, + { + status: "ok", + dnpName: "op-erigon.dnp.dappnode.eth", + avatarUrl: "", + isInstalled: true, + isUpdated: true, + isRunning: true, + data: { + dnpName: "package", + reqVersion: "0.1.0", + semVersion: "0.1.0", + imageFile: { + hash: "QM..", + source: "ipfs", + size: 123 + }, + warnings: {}, + signedSafe: true, + metadata: { + name: "geth.dnp.dappnode.eth", + description: "Go implementation of ethereum. Execution client", + shortDescription: "Go implementation of ethereum", + version: "0.1.0" + } + }, + isSelected: false, + enableHistorical: false + } + ], + rollup: { + status: "ok", + dnpName: "op-node.dnp.dappnode.eth", + avatarUrl: "", + isInstalled: false, + isUpdated: false, + isRunning: true, + data: { + dnpName: "package", + reqVersion: "0.1.0", + semVersion: "0.1.0", + imageFile: { + hash: "QM..", + source: "ipfs", + size: 123 + }, + warnings: {}, + signedSafe: true, + metadata: { + name: "geth.dnp.dappnode.eth", + description: "Go implementation of ethereum. Execution client", + shortDescription: "Go implementation of ethereum", + version: "0.1.0" + } + }, + isSelected: false, + mainnetRpcUrl: "" + }, + archive: { + status: "ok", + dnpName: "op-l2geth.dnp.dappnode.eth", + avatarUrl: "", + isInstalled: false, + isUpdated: false, + isRunning: true, + data: { + dnpName: "package", + reqVersion: "0.1.0", + semVersion: "0.1.0", + imageFile: { + hash: "QM..", + source: "ipfs", + size: 123 + }, + warnings: {}, + signedSafe: true, + metadata: { + name: "geth.dnp.dappnode.eth", + description: "Go implementation of ethereum. Execution client", + shortDescription: "Go implementation of ethereum", + version: "0.1.0" + } + }, + isSelected: true + } }), optimismConfigSet: async () => {} }; From e42248a9f8ff826611da816fc67300b5d899c279 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 16:20:38 +0200 Subject: [PATCH 12/45] implement setOptimismConfig --- .../src/daemons/stakerConfig/index.ts | 2 +- packages/dappmanager/src/db/optimismConfig.ts | 22 +- packages/dappmanager/src/db/package.ts | 10 +- packages/dappmanager/src/db/stakerConfig.ts | 16 +- .../src/modules/optimism/getOptimismConfig.ts | 13 +- .../modules/optimism/getOptimismNodeRpcUrl.ts | 13 ++ .../src/modules/optimism/setOptimismConfig.ts | 188 ++++++++++-------- .../dappmanager/src/utils/getPkgItemData.ts | 4 +- 8 files changed, 136 insertions(+), 132 deletions(-) create mode 100644 packages/dappmanager/src/modules/optimism/getOptimismNodeRpcUrl.ts diff --git a/packages/dappmanager/src/daemons/stakerConfig/index.ts b/packages/dappmanager/src/daemons/stakerConfig/index.ts index 7bc2260fb..1a28e492d 100644 --- a/packages/dappmanager/src/daemons/stakerConfig/index.ts +++ b/packages/dappmanager/src/daemons/stakerConfig/index.ts @@ -14,7 +14,7 @@ async function runStakerCacheUpdate({ const releaseFetcher = new ReleaseFetcher(); const repository = await releaseFetcher.getRelease(dnpName); const dataDnp = pickPackageItemData(repository); - db.optimismItemMetadata.set(dnpName, dataDnp); + db.pkgItemMetadata.set(dnpName, dataDnp); } catch (e) { logs.error("Error on staker cache update daemon", e); } diff --git a/packages/dappmanager/src/db/optimismConfig.ts b/packages/dappmanager/src/db/optimismConfig.ts index fa184a729..da9d4e780 100644 --- a/packages/dappmanager/src/db/optimismConfig.ts +++ b/packages/dappmanager/src/db/optimismConfig.ts @@ -1,28 +1,16 @@ import { ExecutionClientOptimism } from "@dappnode/types"; -import { dbCache, dbMain } from "./dbFactory.js"; +import { dbMain } from "./dbFactory.js"; import { interceptGlobalEnvOnSet } from "./interceptGlobalEnvOnSet.js"; -import { PackageItemData } from "@dappnode/common"; const OP_EXECUTION_CLIENT = "op-execution-client"; const OP_ENABLE_HISTORICAL_RPC = "op-enable-historical-rpc"; -// Cache - -const OPTIMISM_ITEM_METADATA = "optimism-item-metadata"; - -export const optimismItemMetadata = dbCache.indexedByKey< - PackageItemData, - string ->({ - rootKey: OPTIMISM_ITEM_METADATA, - getKey: target => target, - validate: (id, metadata) => - typeof id === "string" && typeof metadata === "object" -}); - // Global env to be in the op-node package export const opExecutionClient = interceptGlobalEnvOnSet( - dbMain.staticKey<ExecutionClientOptimism | null>(OP_EXECUTION_CLIENT, null), + dbMain.staticKey<ExecutionClientOptimism | null | undefined>( + OP_EXECUTION_CLIENT, + null + ), Object.keys({ OP_EXECUTION_CLIENT })[0] ); diff --git a/packages/dappmanager/src/db/package.ts b/packages/dappmanager/src/db/package.ts index f1309dc78..f12989034 100644 --- a/packages/dappmanager/src/db/package.ts +++ b/packages/dappmanager/src/db/package.ts @@ -1,4 +1,4 @@ -import { UpdateAvailable } from "@dappnode/common"; +import { PackageItemData, UpdateAvailable } from "@dappnode/common"; import { dbCache } from "./dbFactory.js"; import { stripDots } from "./dbUtils.js"; @@ -6,6 +6,14 @@ const PACKAGE_GETTING_STARTED_SHOW = "package-getting-started-show"; const PACKAGE_INSTALL_TIME = "package-install-time"; const PACKAGE_LATEST_KNOWN_VERSION = "package-latest-known-version"; const PACKAGE_SENT_DATA = "package-sent-data"; +const PKG_ITEM_METADATA = "pkg-item-metadata"; + +export const pkgItemMetadata = dbCache.indexedByKey<PackageItemData, string>({ + rootKey: PKG_ITEM_METADATA, + getKey: target => target, + validate: (id, metadata) => + typeof id === "string" && typeof metadata === "object" +}); export const packageGettingStartedShow = dbCache.indexedByKey<boolean, string>({ rootKey: PACKAGE_GETTING_STARTED_SHOW, diff --git a/packages/dappmanager/src/db/stakerConfig.ts b/packages/dappmanager/src/db/stakerConfig.ts index df321a676..f8b7cb633 100644 --- a/packages/dappmanager/src/db/stakerConfig.ts +++ b/packages/dappmanager/src/db/stakerConfig.ts @@ -1,5 +1,4 @@ -import { PackageItemData } from "@dappnode/common"; -import { dbCache, dbMain } from "./dbFactory.js"; +import { dbMain } from "./dbFactory.js"; import { interceptGlobalEnvOnSet } from "./interceptGlobalEnvOnSet.js"; import { ConsensusClientMainnet, @@ -12,19 +11,6 @@ import { ExecutionClientLukso } from "@dappnode/types"; -// Cache - -const STAKER_ITEM_METADATA = "staker-item-metadata"; - -export const stakerItemMetadata = dbCache.indexedByKey<PackageItemData, string>( - { - rootKey: STAKER_ITEM_METADATA, - getKey: target => target, - validate: (id, metadata) => - typeof id === "string" && typeof metadata === "object" - } -); - // Mainnet const CONSENSUS_CLIENT_MAINNET = "consensus-client-mainnet"; diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index b77950dbd..4c830e09f 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -6,8 +6,6 @@ import { optimismL2Geth, optimismNode } from "@dappnode/types"; -import { ComposeFileEditor } from "../compose/editor.js"; -import { opNodeRpcUrlEnvName, opNodeServiceName } from "./params.js"; import { ReleaseFetcher } from "../release/index.js"; import { getPkgData } from "../../utils/getPkgItemData.js"; import { @@ -16,6 +14,7 @@ import { getIsUpdated } from "../../calls/fetchDnpRequest.js"; import { fileToGatewayUrl } from "../../utils/distributedFile.js"; +import { getOptimismNodeRpcUrl } from "./getOptimismNodeRpcUrl.js"; export async function getOptimismConfig(): Promise<OptimismConfigGet> { try { @@ -116,13 +115,3 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { throw Error(`Error getting Optimism config: ${e}`); } } - -function getOptimismNodeRpcUrl(): string { - const userSettings = new ComposeFileEditor( - optimismNode, - false - ).getUserSettings(); - return userSettings.environment - ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] - : ""; -} diff --git a/packages/dappmanager/src/modules/optimism/getOptimismNodeRpcUrl.ts b/packages/dappmanager/src/modules/optimism/getOptimismNodeRpcUrl.ts new file mode 100644 index 000000000..12ba8ee83 --- /dev/null +++ b/packages/dappmanager/src/modules/optimism/getOptimismNodeRpcUrl.ts @@ -0,0 +1,13 @@ +import { optimismNode } from "@dappnode/types"; +import { ComposeFileEditor } from "../compose/editor.js"; +import { opNodeRpcUrlEnvName, opNodeServiceName } from "./params.js"; + +export function getOptimismNodeRpcUrl(): string { + const userSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + return userSettings.environment + ? userSettings.environment[opNodeServiceName][opNodeRpcUrlEnvName] + : ""; +} diff --git a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts index b94325908..6d30e683c 100644 --- a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts @@ -13,95 +13,15 @@ import { packageSetEnvironment } from "../../calls/packageSetEnvironment.js"; import { opNodeServiceName, opNodeRpcUrlEnvName } from "./params.js"; export async function setOptimismConfig({ - mainnetRpcUrl, - enableHistorical, - targetOpExecutionClient + archive, + executionClient, + rollup }: OptimismConfigSet): Promise<void> { - // Set new target in db. Must go before op-node package install - await db.opExecutionClient.set(targetOpExecutionClient); - - // op-node - const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); - if (!opNodePackage) { - const userSettings: UserSettings = { - environment: { - [opNodeServiceName]: { - [opNodeRpcUrlEnvName]: mainnetRpcUrl - } - } - }; - // make sure op-node is installed - await packageInstall({ - name: optimismNode, - userSettings: { [optimismNode]: userSettings } - }); - } else { - // Make sure package running - for (const container of opNodePackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - - // check if the current env is the same as the new one - const opNodeUserSettings = new ComposeFileEditor( - optimismNode, - false - ).getUserSettings(); - - if ( - opNodeUserSettings.environment?.[opNodeServiceName]?.[ - opNodeRpcUrlEnvName - ] !== mainnetRpcUrl - ) { - await packageSetEnvironment({ - dnpName: optimismNode, - environmentByService: { - [opNodeServiceName]: { - [opNodeRpcUrlEnvName]: mainnetRpcUrl - } - } - }); - } - } - - //const previousHistorical = db.opEnableHistoricalRpc.get(); - await db.opEnableHistoricalRpc.set(enableHistorical); - - // op Execution clients: op-geth || op-erigon - const targetOpExecutionClientPackage = await listPackageNoThrow({ - dnpName: targetOpExecutionClient - }); - if (!targetOpExecutionClientPackage) { - // make sure target package is installed - await packageInstall({ name: targetOpExecutionClient }); - } else { - // TODO: Remove previous volumes if historical is different (danger removing volumes is too intrusives) - /**if (previousHistorical !== enableHistorical) - await packageRestartVolumes({ dnpName: targetOpExecutionClient });*/ - // Make sure target package is running - for (const container of targetOpExecutionClientPackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - } - // stop previous op execution clients - const previousOpExecutionClients = executionClientsOptimism.filter( - client => client !== targetOpExecutionClient - ); - for (const executionClient of previousOpExecutionClients) { - const executionClientPackage = await listPackageNoThrow({ - dnpName: executionClient - }); - if (executionClientPackage) { - for (const container of executionClientPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } - } - // l2geth; const l2gethPackage = await listPackageNoThrow({ dnpName: optimismL2Geth }); - if (enableHistorical) { + if (archive) { // Install l2geth if (!l2gethPackage) { await packageInstall({ name: optimismL2Geth }); @@ -111,6 +31,8 @@ export async function setOptimismConfig({ if (!container.running) await dockerContainerStart(container.containerName); } + if (db.opEnableHistoricalRpc.get() !== true) + await db.opEnableHistoricalRpc.set(true); } else { // Stop package if (l2gethPackage) { @@ -118,5 +40,103 @@ export async function setOptimismConfig({ if (container.running) await dockerContainerStop(container.containerName); } + if (db.opEnableHistoricalRpc.get() !== false) + await db.opEnableHistoricalRpc.set(false); + } + + // op Execution clients: op-geth || op-erigon + if (executionClient) { + const targetOpExecutionClientPackage = await listPackageNoThrow({ + dnpName: executionClient.dnpName + }); + if (!targetOpExecutionClientPackage) { + // make sure target package is installed + await packageInstall({ name: executionClient.dnpName }); + } else { + // Make sure target package is running + for (const container of targetOpExecutionClientPackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + } + // stop previous op execution clients + const previousOpExecutionClients = executionClientsOptimism.filter( + client => client !== executionClient.dnpName + ); + for (const executionClient of previousOpExecutionClients) { + const executionClientPackage = await listPackageNoThrow({ + dnpName: executionClient + }); + if (executionClientPackage) { + for (const container of executionClientPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } + } else { + // stop all op execution clients + for (const executionClient of executionClientsOptimism) { + const executionClientPackage = await listPackageNoThrow({ + dnpName: executionClient + }); + if (executionClientPackage) { + for (const container of executionClientPackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } + } + } + // Set new target in db. Must go before op-node package install + await db.opExecutionClient.set(executionClient?.dnpName); + + // op-node + const opNodePackage = await listPackageNoThrow({ dnpName: optimismNode }); + if (rollup) { + if (!opNodePackage) { + const userSettings: UserSettings = { + environment: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: rollup.mainnetRpcUrl + } + } + }; + // make sure op-node is installed + await packageInstall({ + name: optimismNode, + userSettings: { [optimismNode]: userSettings } + }); + } else { + // Make sure package running + for (const container of opNodePackage.containers) + if (!container.running) + await dockerContainerStart(container.containerName); + + // check if the current env is the same as the new one + const opNodeUserSettings = new ComposeFileEditor( + optimismNode, + false + ).getUserSettings(); + + if ( + opNodeUserSettings.environment?.[opNodeServiceName]?.[ + opNodeRpcUrlEnvName + ] !== rollup.mainnetRpcUrl + ) { + await packageSetEnvironment({ + dnpName: optimismNode, + environmentByService: { + [opNodeServiceName]: { + [opNodeRpcUrlEnvName]: rollup.mainnetRpcUrl + } + } + }); + } + } + } else { + // Stop package + if (opNodePackage) { + for (const container of opNodePackage.containers) + if (container.running) + await dockerContainerStop(container.containerName); + } } } diff --git a/packages/dappmanager/src/utils/getPkgItemData.ts b/packages/dappmanager/src/utils/getPkgItemData.ts index e4ee6cd2f..9d59411aa 100644 --- a/packages/dappmanager/src/utils/getPkgItemData.ts +++ b/packages/dappmanager/src/utils/getPkgItemData.ts @@ -9,7 +9,7 @@ export async function getPkgData( releaseFetcher: ReleaseFetcher, dnpName: string ): Promise<PackageItemData> { - const cachedDnp = db.optimismItemMetadata.get(dnpName); + const cachedDnp = db.pkgItemMetadata.get(dnpName); if (cachedDnp) { // Update cache in the background eventBus.runStakerCacheUpdate.emit({ dnpName }); @@ -17,7 +17,7 @@ export async function getPkgData( } else { const repository = await releaseFetcher.getRelease(dnpName); const dataDnp = pickPackageItemData(repository); - db.optimismItemMetadata.set(dnpName, dataDnp); + db.pkgItemMetadata.set(dnpName, dataDnp); return dataDnp; } } From 14abb61b5be80ee0721857756cdeb7217a56e57c Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 16:24:22 +0200 Subject: [PATCH 13/45] fix args optimismConfigSet --- packages/dappmanager/src/calls/optimismConfig.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/dappmanager/src/calls/optimismConfig.ts b/packages/dappmanager/src/calls/optimismConfig.ts index 7f94f65b5..f7b9cf1ea 100644 --- a/packages/dappmanager/src/calls/optimismConfig.ts +++ b/packages/dappmanager/src/calls/optimismConfig.ts @@ -18,14 +18,14 @@ import { * @param targetOpExecutionClient this is the client that will be used to connect to the Optimism network */ export async function optimismConfigSet({ - mainnetRpcUrl, - enableHistorical, - targetOpExecutionClient + archive, + executionClient, + rollup }: OptimismConfigSet): Promise<void> { await setOptimismConfig({ - mainnetRpcUrl, - enableHistorical, - targetOpExecutionClient + archive, + executionClient, + rollup }); } From 0ab81c90b6ea2f68524e0d5dacbe5d501dd1201c Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 12 Sep 2023 17:10:43 +0200 Subject: [PATCH 14/45] add frontend struct --- .../src/pages/rollups/components/Optimism.tsx | 4 +++- .../admin-ui/src/pages/rollups/components/Polygon.tsx | 2 +- .../src/pages/rollups/components/RollupsRoot.tsx | 11 +++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index d551fbe68..0d9fc3763 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -1,5 +1,7 @@ import React from "react"; -export default function Optimism() { +export default function Optimism({ description }: { description: string }) { + // Context + return <div>Optimism</div>; } diff --git a/packages/admin-ui/src/pages/rollups/components/Polygon.tsx b/packages/admin-ui/src/pages/rollups/components/Polygon.tsx index 0df36731e..50dbc37e5 100644 --- a/packages/admin-ui/src/pages/rollups/components/Polygon.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Polygon.tsx @@ -1,5 +1,5 @@ import React from "react"; -export default function Polygon() { +export default function Polygon({ description }: { description: string }) { return <div>Polygon</div>; } diff --git a/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx index 468acbcb9..247957297 100644 --- a/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx +++ b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx @@ -14,12 +14,19 @@ const RollupsRoot: React.FC = () => { { subPath: "optimism", title: "Optimism", - component: () => Optimism() + component: () => + Optimism({ + description: "Optimism is a Layer 2 scaling solution for Ethereum." + }) }, { subPath: "polygon", title: "Polygon", - component: () => Polygon() + component: () => + Polygon({ + description: + "Polygon is a protocol and a framework for building and connecting Ethereum-compatible blockchain networks." + }) } ]; From 6f6dc55c8038019b56553a391698d0f39d460408 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Wed, 13 Sep 2023 12:15:23 +0200 Subject: [PATCH 15/45] implemented frontend components --- .../src/pages/rollups/components/Optimism.tsx | 106 +++++++++++- .../src/pages/rollups/components/Polygon.tsx | 5 - .../pages/rollups/components/RollupsRoot.tsx | 10 -- .../src/pages/rollups/components/columns.scss | 90 ++++++++++ .../components/columns/ExecutionClient.tsx | 82 +++++++++ .../rollups/components/columns/LegacyGeth.tsx | 78 +++++++++ .../components/columns/OptimismNode.tsx | 81 +++++++++ .../rollups/components/useOptimismConfig.ts | 161 ++++++++++++++++++ 8 files changed, 596 insertions(+), 17 deletions(-) delete mode 100644 packages/admin-ui/src/pages/rollups/components/Polygon.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/columns.scss create mode 100644 packages/admin-ui/src/pages/rollups/components/columns/ExecutionClient.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/columns/LegacyGeth.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/columns/OptimismNode.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index 0d9fc3763..f8e418a5c 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -1,7 +1,109 @@ import React from "react"; +import { api, useApi } from "api"; +import { ThemeContext } from "App"; +import { responseInterface } from "swr"; +import Card from "components/Card"; +import ErrorView from "components/ErrorView"; +import Loading from "components/Loading"; +import Col from "react-bootstrap/Col"; +import Row from "react-bootstrap/Row"; +import SubTitle from "components/SubTitle"; +import ExecutionClient from "./columns/ExecutionClient"; +import LegacyGeth from "./columns/LegacyGeth"; +import OptimismNode from "./columns/OptimismNode"; +import { useOptimismConfig } from "./useOptimismConfig"; +import "./columns.scss"; export default function Optimism({ description }: { description: string }) { - // Context + const { theme } = React.useContext(ThemeContext); - return <div>Optimism</div>; + const currentOptimismConfigReq = useApi.optimismConfigGet(); + + // hooks + const { + reqStatus, + setReqStatus, + ethRpcUrlError, + setEthRpcUrlError, + newExecClient, + setNewExecClient, + newRollup, + setNewRollup, + newArchive, + setNewArchive, + currentOptimismConfig, + setCurrentOptimismConfig, + changes + } = useOptimismConfig(currentOptimismConfigReq); + + return ( + <div className={theme === "light" ? "optimism-light" : "optimism-dark"}> + {currentOptimismConfigReq.data ? ( + <Card> + <p> + Set up your Optimism node configuration. You will need to: <br /> + (1) Choose an Execution Layer client <br /> + (2) Install the optimism node with an Ethereum RPC endpoint + <br /> + (3) Optional; activate/deactivate archive node for historical tx. + </p> + <br /> + + <p>{description}</p> + + <Row className="staker-network"> + <Col> + <SubTitle>Execution Clients</SubTitle> + {currentOptimismConfigReq.data.executionClients.map( + (executionClient, i) => ( + <ExecutionClient + key={i} + executionClient={executionClient} + setNewExecClient={setNewExecClient} + isSelected={ + executionClient.dnpName === newExecClient?.dnpName + } + /> + ) + )} + </Col> + + <Col> + <SubTitle>Optimism Node</SubTitle> + <OptimismNode + rollup={currentOptimismConfigReq.data.rollup} + setNewRollup={setNewRollup} + isSelected={ + currentOptimismConfigReq.data.rollup.dnpName === + newRollup?.dnpName + } + /> + </Col> + + <Col> + <SubTitle>Legacy Geth</SubTitle> + <LegacyGeth + archive={currentOptimismConfigReq.data.archive} + setNewArchive={setNewArchive} + isSelected={ + currentOptimismConfigReq.data.archive.dnpName === + newArchive?.dnpName + } + /> + </Col> + </Row> + + <hr /> + + <div></div> + </Card> + ) : currentOptimismConfigReq.error ? ( + <ErrorView error={currentOptimismConfigReq.error} hideIcon red /> + ) : currentOptimismConfigReq.isValidating ? ( + <Loading steps={[`Loading Optimism configuration`]} /> + ) : ( + <ErrorView error={"No data found"} hideIcon red /> + )} + </div> + ); } diff --git a/packages/admin-ui/src/pages/rollups/components/Polygon.tsx b/packages/admin-ui/src/pages/rollups/components/Polygon.tsx deleted file mode 100644 index 50dbc37e5..000000000 --- a/packages/admin-ui/src/pages/rollups/components/Polygon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export default function Polygon({ description }: { description: string }) { - return <div>Polygon</div>; -} diff --git a/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx index 247957297..b65911d1c 100644 --- a/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx +++ b/packages/admin-ui/src/pages/rollups/components/RollupsRoot.tsx @@ -1,6 +1,5 @@ import React from "react"; import Optimism from "./Optimism"; -import Polygon from "./Polygon"; import Title from "components/Title"; import { NavLink, Routes, Route } from "react-router-dom"; import { title } from "../data"; @@ -18,15 +17,6 @@ const RollupsRoot: React.FC = () => { Optimism({ description: "Optimism is a Layer 2 scaling solution for Ethereum." }) - }, - { - subPath: "polygon", - title: "Polygon", - component: () => - Polygon({ - description: - "Polygon is a protocol and a framework for building and connecting Ethereum-compatible blockchain networks." - }) } ]; diff --git a/packages/admin-ui/src/pages/rollups/components/columns.scss b/packages/admin-ui/src/pages/rollups/components/columns.scss new file mode 100644 index 000000000..327f1080a --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/columns.scss @@ -0,0 +1,90 @@ +:root { + --eth-multi-client-description-font-size: 0.9rem; + --eth-multi-client-description-color: var(--light-text-color); +} + +.card { + margin-top: 15px; +} + +.staker-buttons { + display: flex; + justify-content: space-between; +} + +.stakers-dark { + .optimism-node, + .execution-client, + .legacy-geth { + border: 2px solid rgba(40, 40, 40, 0.5); + border-bottom: 6px solid rgba(40, 40, 40, 0.75); + .title { + color: var(--color-dark-maintext) !important; + } + &.isSelected { + .title { + color: var(--dappnode-color) !important; + } + border: 2px solid rgba(40, 40, 40, 0.5) !important; + border-bottom: 6px solid var(--dappnode-color) !important; + } + &:hover:not(.isSelected) { + cursor: pointer; + background-color: rgb(85, 85, 85); + } + } +} + +.optimism-node, +.execution-client, +.legacy-geth { + width: 100%; + text-align: center; + align-items: center; + + // For the underline line that highlights on select + border-bottom: 6px solid white; + .title { + font-weight: bold; + font-size: 1.25rem; + transition: color 0.15s; + } + /* .description { + font-size: var(--eth-multi-client-description-font-size); + color: var(--eth-multi-client-description-color); + } */ + + > *:not(:last-child) { + margin-bottom: 1rem; + } + // Button feel + &:hover:not(.isSelected) { + cursor: pointer; + border-color: rgb(218, 218, 218); + background-color: rgb(218, 218, 218); + } + &.isSelected { + border-color: var(--dappnode-color); + // background-image: linear-gradient( + // rgba(255, 0, 0, 0) 0%, + // #daedec 50%, + // #99d9d4 77%, + // #7acac4 87%, + // var(--dappnode-color) + // ); + // background-color: var(--dappnode-color); + .title { + color: var(--dappnode-color); + } + } + transition: color 0.15s, background-color 0.15s, border-color 0.15s, + box-shadow 0.15s; + + .avatar { + --avatar-size: 6rem; + display: flex; + margin: auto; + height: var(--avatar-size); + width: var(--avatar-size); + } +} diff --git a/packages/admin-ui/src/pages/rollups/components/columns/ExecutionClient.tsx b/packages/admin-ui/src/pages/rollups/components/columns/ExecutionClient.tsx new file mode 100644 index 000000000..26df3e378 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/columns/ExecutionClient.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import Card from "components/Card"; +import { prettyDnpName } from "utils/format"; +import { joinCssClass } from "utils/css"; +import { OptimismItem, OptimismItemOk } from "@dappnode/common"; +import defaultAvatar from "img/defaultAvatar.png"; +import errorAvatar from "img/errorAvatarTrim.png"; +import Button from "components/Button"; +import { getInstallerPath } from "pages/installer"; +import { useNavigate } from "react-router-dom"; + +export default function ExecutionClient({ + executionClient, + setNewExecClient, + isSelected, + ...props +}: { + executionClient: OptimismItem<"execution">; + setNewExecClient: React.Dispatch< + React.SetStateAction<OptimismItemOk<"execution"> | undefined> + >; + isSelected: boolean; +}) { + const navigate = useNavigate(); + + return ( + <Card + {...props} + className={`execution-client ${joinCssClass({ isSelected })}`} + onClick={ + executionClient.status === "ok" + ? isSelected + ? () => setNewExecClient(undefined) + : () => setNewExecClient(executionClient) + : undefined + } + shadow={isSelected} + > + {executionClient.status === "ok" ? ( + <div className="avatar"> + <img src={executionClient.avatarUrl || defaultAvatar} alt="avatar" /> + </div> + ) : executionClient.status === "error" ? ( + <div className="avatar"> + <img src={errorAvatar} alt="avatar" /> + </div> + ) : null} + + <div className="title">{prettyDnpName(executionClient.dnpName)} </div> + + {executionClient.status === "ok" && + isSelected && + executionClient.isInstalled && + !executionClient.isUpdated && ( + <> + <Button + onClick={() => + navigate( + `${getInstallerPath(executionClient.dnpName)}/${ + executionClient.dnpName + }` + ) + } + variant="dappnode" + > + UPDATE + </Button> + <br /> + <br /> + </> + )} + + {executionClient.status === "ok" && ( + <div className="description"> + {isSelected && + executionClient.data && + executionClient.data.metadata.shortDescription} + </div> + )} + </Card> + ); +} diff --git a/packages/admin-ui/src/pages/rollups/components/columns/LegacyGeth.tsx b/packages/admin-ui/src/pages/rollups/components/columns/LegacyGeth.tsx new file mode 100644 index 000000000..1f365f7c2 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/columns/LegacyGeth.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { OptimismItem, OptimismItemOk } from "@dappnode/common"; +import Card from "components/Card"; +import { prettyDnpName } from "utils/format"; +import { joinCssClass } from "utils/css"; +import defaultAvatar from "img/defaultAvatar.png"; +import errorAvatar from "img/errorAvatarTrim.png"; +import Button from "components/Button"; +import { getInstallerPath } from "pages/installer"; +import { useNavigate } from "react-router-dom"; + +export default function LegacyGeth({ + archive, + setNewArchive, + isSelected, + ...props +}: { + archive: OptimismItem<"archive">; + setNewArchive: React.Dispatch< + React.SetStateAction<OptimismItemOk<"archive"> | undefined> + >; + isSelected: boolean; +}) { + const navigate = useNavigate(); + + return ( + <Card + {...props} + className={`legacy-geth ${joinCssClass({ isSelected })}`} + shadow={isSelected} + onClick={ + archive.status === "ok" + ? isSelected + ? () => setNewArchive(undefined) + : () => setNewArchive(archive) + : undefined + } + > + {archive.status === "ok" ? ( + <div className="avatar"> + <img src={archive.avatarUrl || defaultAvatar} alt="avatar" /> + </div> + ) : archive.status === "error" ? ( + <div className="avatar"> + <img src={errorAvatar} alt="avatar" /> + </div> + ) : null} + + <div className="title">{prettyDnpName(archive.dnpName)} </div> + + {archive.status === "ok" && + isSelected && + archive.isInstalled && + !archive.isUpdated && ( + <> + <Button + onClick={() => + navigate( + `${getInstallerPath(archive.dnpName)}/${archive.dnpName}` + ) + } + variant="dappnode" + > + UPDATE + </Button> + <br /> + <br /> + </> + )} + + {archive.status === "ok" && ( + <div className="description"> + {isSelected && archive.data && archive.data.metadata.shortDescription} + </div> + )} + </Card> + ); +} diff --git a/packages/admin-ui/src/pages/rollups/components/columns/OptimismNode.tsx b/packages/admin-ui/src/pages/rollups/components/columns/OptimismNode.tsx new file mode 100644 index 000000000..ccbabff0f --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/columns/OptimismNode.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import { OptimismItem, OptimismItemOk } from "@dappnode/common"; +import Card from "components/Card"; +import { prettyDnpName } from "utils/format"; +import { joinCssClass } from "utils/css"; +import defaultAvatar from "img/defaultAvatar.png"; +import errorAvatar from "img/errorAvatarTrim.png"; +import Button from "components/Button"; +import { getInstallerPath } from "pages/installer"; +import { useNavigate } from "react-router-dom"; + +export default function OptimismNode({ + rollup, + setNewRollup, + isSelected, + ...props +}: { + rollup: OptimismItem<"rollup">; + setNewRollup: React.Dispatch< + React.SetStateAction<OptimismItemOk<"rollup"> | undefined> + >; + isSelected: boolean; +}) { + const navigate = useNavigate(); + + return ( + <Card + {...props} + className={`optimism-node ${joinCssClass({ isSelected })}`} + shadow={isSelected} + > + <div + onClick={ + rollup.status === "ok" + ? isSelected + ? () => setNewRollup(undefined) + : () => setNewRollup(rollup) + : undefined + } + > + {rollup.status === "ok" ? ( + <div className="avatar"> + <img src={rollup.avatarUrl || defaultAvatar} alt="avatar" /> + </div> + ) : rollup.status === "error" ? ( + <div className="avatar"> + <img src={errorAvatar} alt="avatar" /> + </div> + ) : null} + + <div className="title">{prettyDnpName(rollup.dnpName)} </div> + </div> + + {rollup.status === "ok" && + isSelected && + rollup.isInstalled && + !rollup.isUpdated && ( + <> + <Button + onClick={() => + navigate( + `${getInstallerPath(rollup.dnpName)}/${rollup.dnpName}` + ) + } + variant="dappnode" + > + UPDATE + </Button> + <br /> + <br /> + </> + )} + + {rollup.status === "ok" && ( + <div className="description"> + {isSelected && rollup.data && rollup.data.metadata.shortDescription} + </div> + )} + </Card> + ); +} diff --git a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts new file mode 100644 index 000000000..34dd530e8 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts @@ -0,0 +1,161 @@ +import { useState, useEffect } from "react"; +import { ReqStatus } from "types"; +import { + OptimismConfigGet, + OptimismConfigSet, + OptimismItemOk, + OptimismType, + OptimismItem +} from "@dappnode/common"; +import { responseInterface } from "swr"; + +export const useOptimismConfig = ( + currentOptimismConfigReq: responseInterface<OptimismConfigGet, Error> +) => { + // Request status + const [reqStatus, setReqStatus] = useState<ReqStatus>({}); + // Error + const [ethRpcUrlError, setEthRpcUrlError] = useState<string | null>(null); + // New config + const [newExecClient, setNewExecClient] = useState< + OptimismItemOk<"execution"> + >(); + const [newRollup, setNewRollup] = useState<OptimismItemOk<"rollup">>(); + const [newArchive, setNewArchive] = useState<OptimismItemOk<"archive">>(); + const [currentOptimismConfig, setCurrentOptimismConfig] = useState< + OptimismConfigSet + >(); + // Changes + const [changes, setChanges] = useState<{ + isAllowed: boolean; + reason?: string; + severity?: "warning" | "secondary" | "danger"; + }>({ isAllowed: false }); + + useEffect(() => { + if (currentOptimismConfigReq.data) { + const { + executionClients, + rollup, + archive + } = currentOptimismConfigReq.data; + + const executionClient = executionClients.find(ec => + isOkSelectedInstalledAndRunning(ec) + ); + + if (executionClient && executionClient.status === "ok") + setNewExecClient(executionClient); + + if (isOkSelectedInstalledAndRunning(rollup) && rollup.status === "ok") + setNewRollup(rollup); + + if (isOkSelectedInstalledAndRunning(archive) && archive.status === "ok") + setNewArchive(archive); + + // Set the current config to be displayed in advance view + setCurrentOptimismConfig({ + executionClient: + executionClient?.status === "ok" ? executionClient : undefined, + rollup: rollup?.status === "ok" ? rollup : undefined, + archive: archive?.status === "ok" ? archive : undefined + }); + } + }, [currentOptimismConfigReq.data]); + + useEffect(() => { + if (currentOptimismConfig) + setChanges( + getChanges({ + currentOptimismConfig, + newExecClient, + newRollup, + newArchive, + ethRpcUrlError + }) + ); + }, [ + currentOptimismConfig, + newExecClient, + newRollup, + newArchive, + ethRpcUrlError + ]); + + return { + reqStatus, + setReqStatus, + ethRpcUrlError, + setEthRpcUrlError, + newExecClient, + setNewExecClient, + newRollup, + setNewRollup, + newArchive, + setNewArchive, + currentOptimismConfig, + setCurrentOptimismConfig, + changes + }; +}; + +function getChanges({ + currentOptimismConfig, + newExecClient, + newRollup, + newArchive, + ethRpcUrlError +}: { + currentOptimismConfig: OptimismConfigSet; + newExecClient?: OptimismItemOk<"execution">; + newRollup?: OptimismItemOk<"rollup">; + newArchive?: OptimismItemOk<"archive">; + ethRpcUrlError?: string | null; +}): { + isAllowed: boolean; + reason?: string; + severity?: "warning" | "secondary" | "danger"; +} { + // Not allowed if ethRpcUrlError + if (ethRpcUrlError) + return { + isAllowed: false, + reason: "Invalid Ethereum RPC url", + severity: "danger" + }; + + const { executionClient, rollup, archive } = currentOptimismConfig; + + // Not allowed if no changes + if ( + executionClient?.dnpName === newExecClient?.dnpName && + Boolean(rollup) === Boolean(newRollup) && + Boolean(archive) === Boolean(newArchive) + ) + return { + isAllowed: false, + reason: "No changes detected", + severity: "secondary" + }; + + // Not allowed if changes AND (Execution Client or Rollup deselected) + if (!newExecClient || !newRollup) + return { + isAllowed: false, + reason: "Execution Client and Rollup are required", + severity: "danger" + }; + + return { isAllowed: true }; +} + +function isOkSelectedInstalledAndRunning<T extends OptimismType>( + item: OptimismItem<T> +): boolean { + return ( + item.status === "ok" && + item.isSelected && + item.isInstalled && + item.isRunning + ); +} From 9856a6072f15c012a121fbb5f1101e11d17580cb Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 13 Sep 2023 15:06:59 +0000 Subject: [PATCH 16/45] Add Ethereum RPC field --- .../src/pages/rollups/components/Optimism.tsx | 45 ++++++++++++++++++- .../rollups/components/useOptimismConfig.ts | 6 +++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index f8e418a5c..acaf71183 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -13,6 +13,8 @@ import LegacyGeth from "./columns/LegacyGeth"; import OptimismNode from "./columns/OptimismNode"; import { useOptimismConfig } from "./useOptimismConfig"; import "./columns.scss"; +import { Alert, Button, Form } from "react-bootstrap"; +import Input from "components/Input"; export default function Optimism({ description }: { description: string }) { const { theme } = React.useContext(ThemeContext); @@ -23,6 +25,8 @@ export default function Optimism({ description }: { description: string }) { const { reqStatus, setReqStatus, + ethRpcUrl, + setEthRpcUrl, ethRpcUrlError, setEthRpcUrlError, newExecClient, @@ -51,6 +55,21 @@ export default function Optimism({ description }: { description: string }) { <p>{description}</p> + <> + <Input + value={ethRpcUrl || ""} + onValueChange={setEthRpcUrl} + isInvalid={Boolean(ethRpcUrlError)} + prepend="Ethereum RPC URL" + placeholder="Ethereum mainnet RPC URL for Optimism node" + /> + {ethRpcUrl && ethRpcUrlError && ( + <Form.Text className="text-danger" as="span"> + {ethRpcUrlError} + </Form.Text> + )} + </> + <Row className="staker-network"> <Col> <SubTitle>Execution Clients</SubTitle> @@ -95,7 +114,31 @@ export default function Optimism({ description }: { description: string }) { <hr /> - <div></div> + <div> + <div className="staker-buttons"> + <Button + variant="dappnode" + disabled={!changes.isAllowed || reqStatus.loading} + onClick={() => /* TODO */ console.log("TODO")} + > + Apply changes + </Button> + </div> + + {!changes.isAllowed && changes.reason && ( + <> + <br /> + <br /> + <Alert variant={changes.severity}> + Cannot apply changes: <b>{changes.reason}</b> + </Alert> + </> + )} + + {reqStatus.error && ( + <ErrorView error={reqStatus.error} hideIcon red /> + )} + </div> </Card> ) : currentOptimismConfigReq.error ? ( <ErrorView error={currentOptimismConfigReq.error} hideIcon red /> diff --git a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts index 34dd530e8..60215ffdc 100644 --- a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts +++ b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts @@ -14,6 +14,10 @@ export const useOptimismConfig = ( ) => { // Request status const [reqStatus, setReqStatus] = useState<ReqStatus>({}); + // Ethereum mainnet RPC url + // TODO: Store it and retrieve it from DB + const [ethRpcUrl, setEthRpcUrl] = useState<string | null>(null); + // Error const [ethRpcUrlError, setEthRpcUrlError] = useState<string | null>(null); // New config @@ -85,6 +89,8 @@ export const useOptimismConfig = ( return { reqStatus, setReqStatus, + ethRpcUrl, + setEthRpcUrl, ethRpcUrlError, setEthRpcUrlError, newExecClient, From dc74811ca7e26f8e5be34ff15c8bb65071c7085b Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 13 Sep 2023 15:32:15 +0000 Subject: [PATCH 17/45] Fix mainnet RPC set --- .../src/pages/rollups/components/Optimism.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index acaf71183..f1897dcdd 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -25,8 +25,6 @@ export default function Optimism({ description }: { description: string }) { const { reqStatus, setReqStatus, - ethRpcUrl, - setEthRpcUrl, ethRpcUrlError, setEthRpcUrlError, newExecClient, @@ -57,13 +55,25 @@ export default function Optimism({ description }: { description: string }) { <> <Input - value={ethRpcUrl || ""} - onValueChange={setEthRpcUrl} + value={currentOptimismConfig?.rollup?.mainnetRpcUrl || ""} + onValueChange={ + (value: string) => { + if (currentOptimismConfig?.rollup){ + setCurrentOptimismConfig({ + ...currentOptimismConfig, + rollup: { + ...currentOptimismConfig.rollup, + mainnetRpcUrl: value, + }, + }) + } + } + } isInvalid={Boolean(ethRpcUrlError)} prepend="Ethereum RPC URL" placeholder="Ethereum mainnet RPC URL for Optimism node" /> - {ethRpcUrl && ethRpcUrlError && ( + {currentOptimismConfig?.rollup?.mainnetRpcUrl && ethRpcUrlError && ( <Form.Text className="text-danger" as="span"> {ethRpcUrlError} </Form.Text> From 7d39a8dc3718e84aa044dc0e83816522e5cd1b96 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Thu, 14 Sep 2023 12:02:32 +0000 Subject: [PATCH 18/45] Added setNewOptimismConfig function --- .../src/pages/rollups/components/Optimism.tsx | 84 ++++++++++++++----- .../rollups/components/useOptimismConfig.ts | 27 ++++-- 2 files changed, 85 insertions(+), 26 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index f1897dcdd..2ab2b7e31 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -15,6 +15,9 @@ import { useOptimismConfig } from "./useOptimismConfig"; import "./columns.scss"; import { Alert, Button, Form } from "react-bootstrap"; import Input from "components/Input"; +import { confirm } from "components/ConfirmDialog"; +import { disclaimer } from "../data"; +import { withToast } from "components/toast/Toast"; export default function Optimism({ description }: { description: string }) { const { theme } = React.useContext(ThemeContext); @@ -26,18 +29,71 @@ export default function Optimism({ description }: { description: string }) { reqStatus, setReqStatus, ethRpcUrlError, - setEthRpcUrlError, newExecClient, setNewExecClient, + customMainnetRpcUrl, + setCustomMainnetRpcUrl, newRollup, setNewRollup, newArchive, setNewArchive, - currentOptimismConfig, - setCurrentOptimismConfig, changes } = useOptimismConfig(currentOptimismConfigReq); + /** + * Set new Optimism config + */ + async function setNewOptimismConfig() { + try { + if (changes) { + await new Promise((resolve: (confirmOnSetConfig: boolean) => void) => { + confirm({ + title: `Optimism configuration`, + text: + "Are you sure you want to implement this Optimism configuration?", + buttons: [ + { + label: "Continue", + onClick: () => resolve(true) + } + ] + }); + }); + await new Promise((resolve: (confirmOnSetConfig: boolean) => void) => { + confirm({ + title: `Disclaimer`, + text: disclaimer, + buttons: [ + { + label: "Continue", + onClick: () => resolve(true) + } + ] + }); + }); + + setReqStatus({ loading: true }); + // TODO: set new Optimism config + await withToast(() => api.optimismConfigSet({}), { + message: `Setting new Optimism configuration...`, + onSuccess: `Successfully set new Optimism configuration`, + onError: `Error setting new Optimism configuration` + }); + setReqStatus({ result: true }); + } + } catch (e) { + setReqStatus({ error: e }); + } finally { + setReqStatus({ loading: true }); + await withToast(() => currentOptimismConfigReq.revalidate(), { + message: `Getting new Optimism staker configuration`, + onSuccess: `Successfully loaded Optimism staker configuration`, + onError: `Error new loading Optimism staker configuration` + }); + setReqStatus({ loading: false }); + } + } + return ( <div className={theme === "light" ? "optimism-light" : "optimism-dark"}> {currentOptimismConfigReq.data ? ( @@ -55,25 +111,13 @@ export default function Optimism({ description }: { description: string }) { <> <Input - value={currentOptimismConfig?.rollup?.mainnetRpcUrl || ""} - onValueChange={ - (value: string) => { - if (currentOptimismConfig?.rollup){ - setCurrentOptimismConfig({ - ...currentOptimismConfig, - rollup: { - ...currentOptimismConfig.rollup, - mainnetRpcUrl: value, - }, - }) - } - } - } - isInvalid={Boolean(ethRpcUrlError)} + value={customMainnetRpcUrl || ""} + onValueChange={(value: string) => setCustomMainnetRpcUrl(value)} + //isInvalid={Boolean(ethRpcUrlError)} prepend="Ethereum RPC URL" placeholder="Ethereum mainnet RPC URL for Optimism node" /> - {currentOptimismConfig?.rollup?.mainnetRpcUrl && ethRpcUrlError && ( + {customMainnetRpcUrl && ethRpcUrlError && ( <Form.Text className="text-danger" as="span"> {ethRpcUrlError} </Form.Text> @@ -129,7 +173,7 @@ export default function Optimism({ description }: { description: string }) { <Button variant="dappnode" disabled={!changes.isAllowed || reqStatus.loading} - onClick={() => /* TODO */ console.log("TODO")} + onClick={() => setNewOptimismConfig()} > Apply changes </Button> diff --git a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts index 60215ffdc..b92da2dbb 100644 --- a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts +++ b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts @@ -14,9 +14,6 @@ export const useOptimismConfig = ( ) => { // Request status const [reqStatus, setReqStatus] = useState<ReqStatus>({}); - // Ethereum mainnet RPC url - // TODO: Store it and retrieve it from DB - const [ethRpcUrl, setEthRpcUrl] = useState<string | null>(null); // Error const [ethRpcUrlError, setEthRpcUrlError] = useState<string | null>(null); @@ -24,6 +21,7 @@ export const useOptimismConfig = ( const [newExecClient, setNewExecClient] = useState< OptimismItemOk<"execution"> >(); + const [customMainnetRpcUrl, setCustomMainnetRpcUrl] = useState<string | null>(null); const [newRollup, setNewRollup] = useState<OptimismItemOk<"rollup">>(); const [newArchive, setNewArchive] = useState<OptimismItemOk<"archive">>(); const [currentOptimismConfig, setCurrentOptimismConfig] = useState< @@ -86,21 +84,29 @@ export const useOptimismConfig = ( ethRpcUrlError ]); + useEffect(() => { + // If the URL is null, then OP Node will use the corresponding RPC to _DAPPNODE_GLOBAL_EXECUTION_CLIENT_MAINNET + if (customMainnetRpcUrl) { + setEthRpcUrlError(validateUrl(customMainnetRpcUrl)); + } else { + setEthRpcUrlError(null); + } + }, [customMainnetRpcUrl]); + return { reqStatus, setReqStatus, - ethRpcUrl, - setEthRpcUrl, ethRpcUrlError, setEthRpcUrlError, newExecClient, setNewExecClient, + customMainnetRpcUrl, + setCustomMainnetRpcUrl, newRollup, setNewRollup, newArchive, setNewArchive, currentOptimismConfig, - setCurrentOptimismConfig, changes }; }; @@ -155,6 +161,15 @@ function getChanges({ return { isAllowed: true }; } +function validateUrl(str: string): string | null { + try { + new URL(str); + return null; + } catch (_) { + return "Invalid URL"; + } +} + function isOkSelectedInstalledAndRunning<T extends OptimismType>( item: OptimismItem<T> ): boolean { From 1209384ec030bde7d0ec5660332f339fb142d51e Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Thu, 14 Sep 2023 12:20:59 +0000 Subject: [PATCH 19/45] Filled optimism config set --- .../src/pages/rollups/components/Optimism.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index 2ab2b7e31..01ec2494b 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -73,12 +73,28 @@ export default function Optimism({ description }: { description: string }) { }); setReqStatus({ loading: true }); - // TODO: set new Optimism config - await withToast(() => api.optimismConfigSet({}), { - message: `Setting new Optimism configuration...`, - onSuccess: `Successfully set new Optimism configuration`, - onError: `Error setting new Optimism configuration` - }); + await withToast( + () => + api.optimismConfigSet({ + archive: newArchive, + rollup: newRollup + ? { + ...newRollup, + mainnetRpcUrl: customMainnetRpcUrl + ? customMainnetRpcUrl + : newRollup?.mainnetRpcUrl + ? newRollup?.mainnetRpcUrl + : "" + } + : undefined, + executionClient: newExecClient + }), + { + message: `Setting new Optimism configuration...`, + onSuccess: `Successfully set new Optimism configuration`, + onError: `Error setting new Optimism configuration` + } + ); setReqStatus({ result: true }); } } catch (e) { From bfa4c21660d70fa961f102ae590541eeafe5df47 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Thu, 14 Sep 2023 14:36:37 +0000 Subject: [PATCH 20/45] Set env RPC to rollup tab RPC field --- packages/admin-ui/src/pages/rollups/components/Optimism.tsx | 5 +---- .../src/pages/rollups/components/useOptimismConfig.ts | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index 01ec2494b..38bac1d3c 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -1,7 +1,6 @@ import React from "react"; import { api, useApi } from "api"; import { ThemeContext } from "App"; -import { responseInterface } from "swr"; import Card from "components/Card"; import ErrorView from "components/ErrorView"; import Loading from "components/Loading"; @@ -83,8 +82,6 @@ export default function Optimism({ description }: { description: string }) { mainnetRpcUrl: customMainnetRpcUrl ? customMainnetRpcUrl : newRollup?.mainnetRpcUrl - ? newRollup?.mainnetRpcUrl - : "" } : undefined, executionClient: newExecClient @@ -129,7 +126,7 @@ export default function Optimism({ description }: { description: string }) { <Input value={customMainnetRpcUrl || ""} onValueChange={(value: string) => setCustomMainnetRpcUrl(value)} - //isInvalid={Boolean(ethRpcUrlError)} + isInvalid={Boolean(ethRpcUrlError)} prepend="Ethereum RPC URL" placeholder="Ethereum mainnet RPC URL for Optimism node" /> diff --git a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts index b92da2dbb..2aa2eeb27 100644 --- a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts +++ b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts @@ -55,6 +55,8 @@ export const useOptimismConfig = ( if (isOkSelectedInstalledAndRunning(archive) && archive.status === "ok") setNewArchive(archive); + if (rollup.mainnetRpcUrl) setCustomMainnetRpcUrl(rollup.mainnetRpcUrl); + // Set the current config to be displayed in advance view setCurrentOptimismConfig({ executionClient: @@ -89,7 +91,7 @@ export const useOptimismConfig = ( if (customMainnetRpcUrl) { setEthRpcUrlError(validateUrl(customMainnetRpcUrl)); } else { - setEthRpcUrlError(null); + setEthRpcUrlError("You need to set an Ethereum mainnet full node in the Stakers menu (execution + consensus clients) or set a custom RPC URL)"); } }, [customMainnetRpcUrl]); From a25a0357a8a08b60cd89167e1682bf29f4af9e4b Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Fri, 15 Sep 2023 11:34:19 +0200 Subject: [PATCH 21/45] test commit --- .../src/modules/optimism/getOptimismConfig.ts | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index 4c830e09f..6d62937c9 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -22,17 +22,23 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { const currentOptimismExecutionClient = db.opExecutionClient.get(); const enableHistorical = db.opEnableHistoricalRpc.get(); - const mainnetRpcUrl = getOptimismNodeRpcUrl(); const dnpList = await listPackages(); return { executionClients: await Promise.all( executionClientsOptimism.map(async execClient => { try { - if (!(await releaseFetcher.repoExists(execClient))) - throw Error(`Repository ${execClient} does not exist`); + /**if (!(await releaseFetcher.repoExists(execClient))) + throw Error(`Repository ${execClient} does not exist`);*/ - const pkgData = await getPkgData(releaseFetcher, execClient); + let hash = ""; + if (execClient === "op-geth.dnp.dappnode.eth") { + hash = "/ipfs/QmURvP2uKPKyBFpkwFE8JstB8eDcQ2Xd9PmW9TMCPPKpSL"; + } else { + hash = "/ipfs/QmcdDtv2n3ottwn3tvpMiBbLfVMSp1KCHzTzjyLRL6PdqL"; + } + + const pkgData = await getPkgData(releaseFetcher, hash); return { status: "ok", @@ -58,9 +64,13 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { rollup: await new Promise<OptimismItem<"rollup">>(resolve => { (async () => { try { - if (!(await releaseFetcher.repoExists(optimismNode))) - throw Error(`Repository ${optimismNode} does not exist`); - const pkgData = await getPkgData(releaseFetcher, optimismNode); + /**if (!(await releaseFetcher.repoExists(optimismNode))) + throw Error(`Repository ${optimismNode} does not exist`);*/ + + const hash = "/ipfs/QmT7tBCdPX6HLQXmQynuETkwAPCSeAqFbANVKaFJnnQ6nw"; + + const pkgData = await getPkgData(releaseFetcher, hash); + const mainnetRpcUrl = getOptimismNodeRpcUrl(); const isRunning = getIsRunning(pkgData, dnpList); resolve({ status: "ok", @@ -78,7 +88,7 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { status: "error", dnpName: optimismNode, error, - mainnetRpcUrl + mainnetRpcUrl: "" }); } })(); @@ -87,9 +97,12 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { archive: await new Promise<OptimismItem<"archive">>(resolve => { (async () => { try { - if (!(await releaseFetcher.repoExists(optimismL2Geth))) - throw Error(`Repository ${optimismL2Geth} does not exist`); - const pkgData = await getPkgData(releaseFetcher, optimismL2Geth); + /**if (!(await releaseFetcher.repoExists(optimismL2Geth))) + throw Error(`Repository ${optimismL2Geth} does not exist`);*/ + + const hash = "/ipfs/QmWm346aWuktihXB4jQ5QyBfyx1wPdcyXkrRbrq3tKyVnd"; + + const pkgData = await getPkgData(releaseFetcher, hash); const isRunning = getIsRunning(pkgData, dnpList); resolve({ status: "ok", From 8369b6fce563b4728d8ab22eb2a478761ed060c0 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Tue, 19 Sep 2023 15:27:40 +0000 Subject: [PATCH 22/45] Fix allowed selections in OP tab --- .../rollups/components/useOptimismConfig.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts index 2aa2eeb27..9fd904ab7 100644 --- a/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts +++ b/packages/admin-ui/src/pages/rollups/components/useOptimismConfig.ts @@ -152,11 +152,27 @@ function getChanges({ severity: "secondary" }; - // Not allowed if changes AND (Execution Client or Rollup deselected) - if (!newExecClient || !newRollup) + // Not allowed if only Rollup is selected + if (!newExecClient && newRollup) return { isAllowed: false, - reason: "Execution Client and Rollup are required", + reason: "OP Node selected without an Execution Client", + severity: "danger" + }; + + // Not allowed if Execution Client is selected without Rollup + if (newExecClient && !newRollup) + return { + isAllowed: false, + reason: "Execution Client selected without OP Node", + severity: "danger" + }; + + // Not allowed if Archive is selected without both Execution Client and Rollup + if (newArchive && (!newExecClient || !newRollup)) + return { + isAllowed: false, + reason: "Execution Client and OP Node are required to select Archive", severity: "danger" }; From 757c7fd02d9d2c0683d183aa162a436ef5c83805 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 19 Sep 2023 18:09:03 +0200 Subject: [PATCH 23/45] add modules ui to db --- packages/dappmanager/src/db/ui.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/dappmanager/src/db/ui.ts b/packages/dappmanager/src/db/ui.ts index eefce140f..230e7661c 100644 --- a/packages/dappmanager/src/db/ui.ts +++ b/packages/dappmanager/src/db/ui.ts @@ -1,7 +1,13 @@ import { dbMain } from "./dbFactory.js"; -import { NewFeatureId, NewFeatureStatus } from "@dappnode/common"; +import { + NewFeatureId, + NewFeatureStatus, + UiModuleId, + UiModuleStatus +} from "@dappnode/common"; const NEW_FEATURE_STATUS = "new-feature-status"; +const UI_MODULES = "ui-modules"; export const newFeatureStatus = dbMain.indexedByKey< NewFeatureStatus, @@ -10,3 +16,8 @@ export const newFeatureStatus = dbMain.indexedByKey< rootKey: NEW_FEATURE_STATUS, getKey: featureId => featureId }); + +export const uiModules = dbMain.indexedByKey<UiModuleStatus, UiModuleId>({ + rootKey: UI_MODULES, + getKey: moduleId => moduleId +}); From 7cf37d865c0d7dbd2cd748cbb3d9b596fa9727cd Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 19 Sep 2023 18:09:11 +0200 Subject: [PATCH 24/45] add types to common --- packages/common/src/types.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 5f5af990a..bb6901989 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1360,3 +1360,13 @@ export interface RpcResponse<R = any> { // eslint-disable-next-line @typescript-eslint/no-explicit-any error?: { code: number; message: string; data?: any }; } + +/** + * ========== + * MODULES UI + * ========== + */ + +export type UiModuleStatus = "enabled" | "disabled"; + +export type UiModuleId = "stakers" | "rollups"; From 62cdd0dbb2ff843d9fed32f4457fb3abdd7ce575 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Tue, 19 Sep 2023 18:09:25 +0200 Subject: [PATCH 25/45] add basic component modules to dashboard --- .../pages/dashboard/components/Dashboard.tsx | 22 +++++++++---------- .../pages/dashboard/components/Modules.tsx | 5 +++++ .../pages/dashboard/components/dashboard.scss | 20 ----------------- 3 files changed, 15 insertions(+), 32 deletions(-) create mode 100644 packages/admin-ui/src/pages/dashboard/components/Modules.tsx diff --git a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx index 1a3ca2101..c35e17ecd 100644 --- a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx @@ -9,26 +9,24 @@ import SubTitle from "components/SubTitle"; import Title from "components/Title"; import "./dashboard.scss"; +import Modules from "./Modules"; export default function Dashboard() { return ( <> <Title title={title} /> - <div className="dashboard-layout"> - <div className="dashboard-right"> - <SubTitle>Package updates</SubTitle> - <PackageUpdates /> - </div> + <SubTitle>Package updates</SubTitle> + <PackageUpdates /> - <div className="dashboard-left"> - <SubTitle>Chains</SubTitle> - <ChainCards /> + <SubTitle>Modules</SubTitle> + <Modules /> - <SubTitle>Machine stats</SubTitle> - <HostStats /> - </div> - </div> + <SubTitle>Chains</SubTitle> + <ChainCards /> + + <SubTitle>Machine stats</SubTitle> + <HostStats /> </> ); } diff --git a/packages/admin-ui/src/pages/dashboard/components/Modules.tsx b/packages/admin-ui/src/pages/dashboard/components/Modules.tsx new file mode 100644 index 000000000..2c12c9d42 --- /dev/null +++ b/packages/admin-ui/src/pages/dashboard/components/Modules.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function Modules() { + return <div>Modules</div>; +} diff --git a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss index 95c845d98..a570a2b3f 100644 --- a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss +++ b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss @@ -1,23 +1,3 @@ -.dashboard-layout { - display: grid; - grid-gap: var(--default-spacing); - grid-template-columns: 60% auto; - grid-template-rows: auto; - grid-template-areas: "left right"; - - @media screen and (max-width: 65rem) { - display: block; - } -} - -.dashboard-right { - grid-area: right; -} - -.dashboard-left { - grid-area: left; -} - .dashboard-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); From 83aa38d8522a5398fb76de13791106e066c05580 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 20 Sep 2023 09:05:09 +0000 Subject: [PATCH 26/45] Refactor setOpConfig --- packages/common/src/routes.ts | 8 +- .../src/modules/optimism/setOptimismConfig.ts | 104 +++++++++--------- 2 files changed, 57 insertions(+), 55 deletions(-) diff --git a/packages/common/src/routes.ts b/packages/common/src/routes.ts index e6bbbec17..000a283c7 100644 --- a/packages/common/src/routes.ts +++ b/packages/common/src/routes.ts @@ -629,20 +629,20 @@ export interface Routes { */ sshPortGet: () => Promise<number>; /** - * Change the SHH port on the Dappnode host + * Change the SHH port on the DAppNode host */ sshPortSet: (kwargs: { port: number }) => Promise<void>; /** - * Disable or enable SSH on the Dappnode host + * Disable or enable SSH on the DAppNode host */ sshStatusSet: (kwargs: { status: ShhStatus }) => Promise<void>; /** - * Check if SSH is enabled of disabled in the Dappnode host + * Check if SSH is enabled of disabled in the DAppNode host */ sshStatusGet: () => Promise<ShhStatus>; /** - * Returns the current Dappnode system info + * Returns the current DAppNode system info */ systemInfoGet: () => Promise<SystemInfo>; diff --git a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts index 6d30e683c..414b46355 100644 --- a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts @@ -1,10 +1,11 @@ -import { OptimismConfigSet, UserSettings } from "@dappnode/common"; +import { InstalledPackageData, OptimismConfigSet, UserSettings } from "@dappnode/common"; import * as db from "../../db/index.js"; import { listPackageNoThrow } from "../docker/list/listPackages.js"; import { optimismNode, optimismL2Geth, - executionClientsOptimism + executionClientsOptimism, + ExecutionClientOptimism } from "@dappnode/types"; import { ComposeFileEditor } from "../compose/editor.js"; import { packageInstall } from "../../calls/packageInstall.js"; @@ -21,25 +22,20 @@ export async function setOptimismConfig({ const l2gethPackage = await listPackageNoThrow({ dnpName: optimismL2Geth }); + if (archive) { // Install l2geth if (!l2gethPackage) { await packageInstall({ name: optimismL2Geth }); } else { - // Make sure package running - for (const container of l2gethPackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); + await startAllContainers(l2gethPackage); } if (db.opEnableHistoricalRpc.get() !== true) await db.opEnableHistoricalRpc.set(true); } else { - // Stop package - if (l2gethPackage) { - for (const container of l2gethPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } + if (l2gethPackage) + await stopAllContainers([l2gethPackage]); + if (db.opEnableHistoricalRpc.get() !== false) await db.opEnableHistoricalRpc.set(false); } @@ -53,37 +49,14 @@ export async function setOptimismConfig({ // make sure target package is installed await packageInstall({ name: executionClient.dnpName }); } else { - // Make sure target package is running - for (const container of targetOpExecutionClientPackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); - } - // stop previous op execution clients - const previousOpExecutionClients = executionClientsOptimism.filter( - client => client !== executionClient.dnpName - ); - for (const executionClient of previousOpExecutionClients) { - const executionClientPackage = await listPackageNoThrow({ - dnpName: executionClient - }); - if (executionClientPackage) { - for (const container of executionClientPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } + await startAllContainers(targetOpExecutionClientPackage); } + + await stopOtherOpExecutionClients(executionClient.dnpName); + } else { // stop all op execution clients - for (const executionClient of executionClientsOptimism) { - const executionClientPackage = await listPackageNoThrow({ - dnpName: executionClient - }); - if (executionClientPackage) { - for (const container of executionClientPackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } - } + await stopPkgsByDnpNames([...executionClientsOptimism]); } // Set new target in db. Must go before op-node package install await db.opExecutionClient.set(executionClient?.dnpName); @@ -105,10 +78,8 @@ export async function setOptimismConfig({ userSettings: { [optimismNode]: userSettings } }); } else { - // Make sure package running - for (const container of opNodePackage.containers) - if (!container.running) - await dockerContainerStart(container.containerName); + + await startAllContainers(opNodePackage); // check if the current env is the same as the new one const opNodeUserSettings = new ComposeFileEditor( @@ -118,7 +89,7 @@ export async function setOptimismConfig({ if ( opNodeUserSettings.environment?.[opNodeServiceName]?.[ - opNodeRpcUrlEnvName + opNodeRpcUrlEnvName ] !== rollup.mainnetRpcUrl ) { await packageSetEnvironment({ @@ -132,11 +103,42 @@ export async function setOptimismConfig({ } } } else { - // Stop package - if (opNodePackage) { - for (const container of opNodePackage.containers) - if (container.running) - await dockerContainerStop(container.containerName); - } + if (opNodePackage) + stopAllContainers([opNodePackage]); } } + +async function stopOtherOpExecutionClients( + executionClient: ExecutionClientOptimism +): Promise<void> { + const otherOpExecutionClientDnps = executionClientsOptimism.filter( + client => client !== executionClient + ); + + await stopPkgsByDnpNames(otherOpExecutionClientDnps); +} + +async function stopPkgsByDnpNames( + dnpNames: ExecutionClientOptimism[] +) { + const pkgs: (InstalledPackageData | null)[] = await Promise.all( + dnpNames.map(async dnpName => { + return await listPackageNoThrow({ dnpName }); + }) + ); + + // Remove null values + stopAllContainers(pkgs.filter(Boolean) as InstalledPackageData[]); +} + +async function stopAllContainers(pkgs: InstalledPackageData[]): Promise<void> { + for (const pkg of pkgs) + for (const container of pkg.containers) + if (container.running) await dockerContainerStop(container.containerName); +} + +async function startAllContainers(pkg: InstalledPackageData): Promise<void> { + for (const container of pkg.containers) + if (!container.running) + await dockerContainerStart(container.containerName); +} \ No newline at end of file From 5629141db6ffa0682f7de029fb3a5ea5a7c09bb5 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 20 Sep 2023 09:35:00 +0000 Subject: [PATCH 27/45] Add OP global envs --- packages/dappmanager/src/modules/globalEnvs.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/dappmanager/src/modules/globalEnvs.ts b/packages/dappmanager/src/modules/globalEnvs.ts index c3503ab69..08045298e 100644 --- a/packages/dappmanager/src/modules/globalEnvs.ts +++ b/packages/dappmanager/src/modules/globalEnvs.ts @@ -57,7 +57,9 @@ export function computeGlobalEnvsFromDb<B extends boolean>( [`${prefix}CONSENSUS_CLIENT_LUKSO`]: db.consensusClientLukso.get(), [`${prefix}EXECUTION_CLIENT_LUKSO`]: db.executionClientLukso.get(), [`${prefix}MEVBOOST_LUKSO`]: db.mevBoostLukso.get(), - [`${prefix}FEE_RECIPIENT_LUKSO`]: db.feeRecipientLukso.get() + [`${prefix}FEE_RECIPIENT_LUKSO`]: db.feeRecipientLukso.get(), + [`${prefix}OP_ENABLE_HISTORICAL_RPC`]: db.opEnableHistoricalRpc.get(), + [`${prefix}OP_EXECUTION_CLIENT`]: db.opExecutionClient.get(), // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; } From 05c910e7291834bd5d382f2963e35d0470429b70 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 20 Sep 2023 12:34:15 +0000 Subject: [PATCH 28/45] Allow custom L1 RPC --- .../src/modules/optimism/params.ts | 12 +++++++- .../src/modules/optimism/setOptimismConfig.ts | 28 ++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/dappmanager/src/modules/optimism/params.ts b/packages/dappmanager/src/modules/optimism/params.ts index 1579df4d0..43ea431a0 100644 --- a/packages/dappmanager/src/modules/optimism/params.ts +++ b/packages/dappmanager/src/modules/optimism/params.ts @@ -1,2 +1,12 @@ -export const opNodeRpcUrlEnvName = "L1_RPC"; +import { ExecutionClientOptimism } from "@dappnode/types"; + +export const opNodeRpcUrlEnvName = "CUSTOM_L1_RPC"; export const opNodeServiceName = "op-node"; + +export const opExecutionClientHistoricalRpcUrlEnvName = "HISTORICAL_RPC_URL"; +export const historicalRpcUrl = "http://op-l2geth.dappnode:8545"; + +export const opClientToServiceMap: Record<ExecutionClientOptimism, string> = { + "op-geth.dnp.dappnode.eth": "geth", + "op-erigon.dnp.dappnode.eth": "erigon" +}; \ No newline at end of file diff --git a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts index 414b46355..cff536012 100644 --- a/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/setOptimismConfig.ts @@ -11,7 +11,7 @@ import { ComposeFileEditor } from "../compose/editor.js"; import { packageInstall } from "../../calls/packageInstall.js"; import { dockerContainerStart, dockerContainerStop } from "../docker/index.js"; import { packageSetEnvironment } from "../../calls/packageSetEnvironment.js"; -import { opNodeServiceName, opNodeRpcUrlEnvName } from "./params.js"; +import { opNodeServiceName, opNodeRpcUrlEnvName, historicalRpcUrl, opExecutionClientHistoricalRpcUrlEnvName, opClientToServiceMap } from "./params.js"; export async function setOptimismConfig({ archive, @@ -45,11 +45,30 @@ export async function setOptimismConfig({ const targetOpExecutionClientPackage = await listPackageNoThrow({ dnpName: executionClient.dnpName }); + + const userSettings: UserSettings = { + environment: { + [opClientToServiceMap[executionClient.dnpName]]: { + [opExecutionClientHistoricalRpcUrlEnvName]: historicalRpcUrl // TODO: Empty if not archive? + } + } + }; + if (!targetOpExecutionClientPackage) { + // make sure target package is installed - await packageInstall({ name: executionClient.dnpName }); + await packageInstall({ + name: executionClient.dnpName, + userSettings: { [executionClient.dnpName]: userSettings } + }); } else { + await packageSetEnvironment({ + dnpName: executionClient.dnpName, + environmentByService: userSettings.environment ? userSettings.environment : {} + }); + await startAllContainers(targetOpExecutionClientPackage); + } await stopOtherOpExecutionClients(executionClient.dnpName); @@ -58,6 +77,7 @@ export async function setOptimismConfig({ // stop all op execution clients await stopPkgsByDnpNames([...executionClientsOptimism]); } + // Set new target in db. Must go before op-node package install await db.opExecutionClient.set(executionClient?.dnpName); @@ -104,7 +124,7 @@ export async function setOptimismConfig({ } } else { if (opNodePackage) - stopAllContainers([opNodePackage]); + await stopAllContainers([opNodePackage]); } } @@ -128,7 +148,7 @@ async function stopPkgsByDnpNames( ); // Remove null values - stopAllContainers(pkgs.filter(Boolean) as InstalledPackageData[]); + await stopAllContainers(pkgs.filter(Boolean) as InstalledPackageData[]); } async function stopAllContainers(pkgs: InstalledPackageData[]): Promise<void> { From 4d05aeb4857bb8eb87bcc0d29456eae9a2f820e4 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Wed, 20 Sep 2023 16:14:57 +0200 Subject: [PATCH 29/45] remove usage context --- .../src/components/sidebar/navbarItems.ts | 67 ++++++++++--------- .../topbar/dropdownMenus/UsageSwitch.tsx | 39 ----------- .../src/pages/packages/pages/ById.tsx | 15 +---- .../stakers/components/StakerNetwork.tsx | 9 ++- .../pages/system/components/SystemRoot.tsx | 17 +---- packages/common/src/types.ts | 10 --- packages/dappmanager/src/db/ui.ts | 7 -- 7 files changed, 46 insertions(+), 118 deletions(-) delete mode 100644 packages/admin-ui/src/components/topbar/dropdownMenus/UsageSwitch.tsx diff --git a/packages/admin-ui/src/components/sidebar/navbarItems.ts b/packages/admin-ui/src/components/sidebar/navbarItems.ts index 609e7a449..ad0022133 100644 --- a/packages/admin-ui/src/components/sidebar/navbarItems.ts +++ b/packages/admin-ui/src/components/sidebar/navbarItems.ts @@ -54,71 +54,76 @@ export const fundedBy: { logo: string; text: string; link: string }[] = [ } ]; -export const advancedItems: { - name: string; - href: string; - icon: (props: any) => JSX.Element; -}[] = [ - { - name: "Community", - href: communityRelativePath, - icon: MdPeople - }, - { - name: "Sdk", - href: sdkRelativePath, - icon: MdBuild - }, - { - name: "Support", - href: supportRelativePath, - icon: MdHelp - } -]; - -export const basicItems: { +export const sidenavItems: { name: string; href: string; icon: (props: any) => JSX.Element; + show: boolean; }[] = [ { name: "Dashboard", href: dashboardRelativePath, - icon: MdDashboard + icon: MdDashboard, + show: true }, { name: "Wi-Fi", href: wifiRelativePath, - icon: MdWifi + icon: MdWifi, + show: true }, { name: "VPN", href: devicesRelativePath, - icon: MdDevices + icon: MdDevices, + show: true }, { name: "DAppStore", href: installerRelativePath, - icon: MdCreateNewFolder + icon: MdCreateNewFolder, + show: true }, { name: "Packages", href: packagesRelativePath, - icon: MdFolder + icon: MdFolder, + show: true }, { name: "Stakers", href: stakersRelativePath, - icon: SiEthereum + icon: SiEthereum, + show: true }, { name: "Repository", href: repositoryRelativePath, - icon: BiGitRepoForked + icon: BiGitRepoForked, + show: true }, { name: "System", href: systemRelativePath, - icon: MdSettings + icon: MdSettings, + show: true + }, + { + name: "Community", + href: communityRelativePath, + icon: MdPeople, + show: true + }, + { + name: "Sdk", + href: sdkRelativePath, + icon: MdBuild, + show: true + }, + { + name: "Support", + href: supportRelativePath, + icon: MdHelp, + show: true } ]; diff --git a/packages/admin-ui/src/components/topbar/dropdownMenus/UsageSwitch.tsx b/packages/admin-ui/src/components/topbar/dropdownMenus/UsageSwitch.tsx deleted file mode 100644 index 0af7d3c25..000000000 --- a/packages/admin-ui/src/components/topbar/dropdownMenus/UsageSwitch.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { BiWrench } from "react-icons/bi"; -import { IoMdSettings } from "react-icons/io"; -import React from "react"; -import "./dropdown.scss"; -import { UsageContext } from "App"; -import Tooltip from "react-bootstrap/Tooltip"; -import OverlayTrigger from "react-bootstrap/OverlayTrigger"; - -export default function UsageSwitch({ - toggleUsage -}: { - toggleUsage: () => void; -}) { - const { usage } = React.useContext(UsageContext); - return ( - <div className="tn-dropdown"> - <OverlayTrigger - placement={"bottom"} - overlay={ - <Tooltip id={`tooltip-usage`}> - Display {usage === "advanced" ? "basic usage" : "advanced usage"} - </Tooltip> - } - > - <button - style={{ border: "none", background: "none" }} - className="tn-dropdown-toggle" - onClick={toggleUsage} - > - {usage === "advanced" ? ( - <BiWrench style={{ fontSize: "larger" }} /> - ) : ( - <IoMdSettings style={{ fontSize: "larger" }} /> - )} - </button> - </OverlayTrigger> - </div> - ); -} diff --git a/packages/admin-ui/src/pages/packages/pages/ById.tsx b/packages/admin-ui/src/pages/packages/pages/ById.tsx index 8ff6e113f..1d3864713 100644 --- a/packages/admin-ui/src/pages/packages/pages/ById.tsx +++ b/packages/admin-ui/src/pages/packages/pages/ById.tsx @@ -18,12 +18,10 @@ import Title from "components/Title"; // Utils import { prettyDnpName } from "utils/format"; import { AlertPackageUpdateAvailable } from "../components/AlertPackageUpdateAvailable"; -import { UsageContext } from "App"; export const PackageById: React.FC = () => { const params = useParams(); const id = params.id || ""; - const { usage } = React.useContext(UsageContext); const dnpRequest = useApi.packageGet({ dnpName: id }); const dnp = dnpRequest.data; @@ -63,7 +61,7 @@ export const PackageById: React.FC = () => { * - Route (render, path) */ - const basicRoutes: { + const availableRoutes: { name: string; subPath: string; render: () => JSX.Element; @@ -75,13 +73,7 @@ export const PackageById: React.FC = () => { <Info dnp={dnp} {...{ manifest, gettingStarted, gettingStartedShow }} /> ), available: true - } - ].filter(route => route.available); - const advancedRoutes: { - name: string; - subPath: string; - render: () => JSX.Element; - }[] = [ + }, { name: "Config", subPath: "config", @@ -116,9 +108,6 @@ export const PackageById: React.FC = () => { } ].filter(route => route.available); - const availableRoutes = - usage === "advanced" ? [...basicRoutes, ...advancedRoutes] : basicRoutes; - return ( <> <Title title={title} subtitle={prettyDnpName(dnpName)} /> diff --git a/packages/admin-ui/src/pages/stakers/components/StakerNetwork.tsx b/packages/admin-ui/src/pages/stakers/components/StakerNetwork.tsx index 0cbdcab89..f23333fce 100644 --- a/packages/admin-ui/src/pages/stakers/components/StakerNetwork.tsx +++ b/packages/admin-ui/src/pages/stakers/components/StakerNetwork.tsx @@ -18,7 +18,7 @@ import Loading from "components/Loading"; import { responseInterface } from "swr"; import { Alert, Form } from "react-bootstrap"; import "./columns.scss"; -import { ThemeContext } from "App"; +import { AppContext } from "App"; import LaunchpadValidators from "./launchpad/LaunchpadValidators"; import { FaEthereum } from "react-icons/fa"; import Input from "components/Input"; @@ -33,7 +33,7 @@ export default function StakerNetwork<T extends Network>({ description: string; }) { // Context - const { theme } = React.useContext(ThemeContext); + const { theme } = React.useContext(AppContext); const currentStakerConfigReq = useApi.stakerConfigGet( network @@ -204,7 +204,10 @@ export default function StakerNetwork<T extends Network>({ (consensusClient, i) => ( <ConsensusClient<T> key={i} - consensusClient={{...consensusClient, useCheckpointSync: true}} + consensusClient={{ + ...consensusClient, + useCheckpointSync: true + }} setNewConsClient={setNewConsClient} isSelected={ consensusClient.dnpName === newConsClient?.dnpName diff --git a/packages/admin-ui/src/pages/system/components/SystemRoot.tsx b/packages/admin-ui/src/pages/system/components/SystemRoot.tsx index 7899eab66..13e94ef9d 100644 --- a/packages/admin-ui/src/pages/system/components/SystemRoot.tsx +++ b/packages/admin-ui/src/pages/system/components/SystemRoot.tsx @@ -15,11 +15,9 @@ import { Network } from "./Network"; import { Advanced } from "./Advanced"; import { Notifications } from "./Notifications"; import Hardware from "./Hardware"; -import { UsageContext } from "App"; const SystemRoot: React.FC = () => { - const { usage } = React.useContext(UsageContext); - const basicRoutes: { + const availableRoutes: { name: string; subLink: string; subPath: string; @@ -49,15 +47,7 @@ const SystemRoot: React.FC = () => { subLink: subPaths.power, subPath: subPaths.power, component: PowerManagment - } - ]; - const advancedRoutes: { - name: string; - subLink: string; - subPath: string; - component: React.ComponentType<any>; - hideFromMenu?: boolean; - }[] = [ + }, { name: "Notifications", subLink: subPaths.notifications, @@ -111,9 +101,6 @@ const SystemRoot: React.FC = () => { } ]; - const availableRoutes = - usage === "advanced" ? [...basicRoutes, ...advancedRoutes] : basicRoutes; - return ( <> <Title title={title} /> diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index bb6901989..5f5af990a 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1360,13 +1360,3 @@ export interface RpcResponse<R = any> { // eslint-disable-next-line @typescript-eslint/no-explicit-any error?: { code: number; message: string; data?: any }; } - -/** - * ========== - * MODULES UI - * ========== - */ - -export type UiModuleStatus = "enabled" | "disabled"; - -export type UiModuleId = "stakers" | "rollups"; diff --git a/packages/dappmanager/src/db/ui.ts b/packages/dappmanager/src/db/ui.ts index 230e7661c..567712d4e 100644 --- a/packages/dappmanager/src/db/ui.ts +++ b/packages/dappmanager/src/db/ui.ts @@ -2,12 +2,9 @@ import { dbMain } from "./dbFactory.js"; import { NewFeatureId, NewFeatureStatus, - UiModuleId, - UiModuleStatus } from "@dappnode/common"; const NEW_FEATURE_STATUS = "new-feature-status"; -const UI_MODULES = "ui-modules"; export const newFeatureStatus = dbMain.indexedByKey< NewFeatureStatus, @@ -17,7 +14,3 @@ export const newFeatureStatus = dbMain.indexedByKey< getKey: featureId => featureId }); -export const uiModules = dbMain.indexedByKey<UiModuleStatus, UiModuleId>({ - rootKey: UI_MODULES, - getKey: moduleId => moduleId -}); From 05e2984084e0a1140980ce4423ad8cd421a8f220 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Wed, 20 Sep 2023 16:15:07 +0200 Subject: [PATCH 30/45] add modules context --- packages/admin-ui/package.json | 2 +- packages/admin-ui/src/App.tsx | 168 +++++++++++------- .../admin-ui/src/__mock-backend__/index.ts | 10 +- .../src/components/sidebar/SideBar.tsx | 44 ++--- .../src/components/sidebar/sidebar.scss | 22 +++ .../topbar/dropdownMenus/ThemeSwitch.tsx | 4 +- .../pages/dashboard/components/Dashboard.tsx | 37 ++-- .../pages/dashboard/components/Modules.tsx | 60 ++++++- .../pages/dashboard/components/dashboard.scss | 31 ++++ packages/admin-ui/src/params.ts | 4 +- packages/admin-ui/src/types.ts | 19 ++ 11 files changed, 292 insertions(+), 109 deletions(-) diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json index b8fc70d15..6630e8390 100644 --- a/packages/admin-ui/package.json +++ b/packages/admin-ui/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@dappnode/common": "^0.1.0", - "@dappnode/eventbus": "^0.1.0", "@dappnode/dappmanager": "^0.1.0", + "@dappnode/eventbus": "^0.1.0", "@dappnode/types": "^0.1.25", "@reduxjs/toolkit": "^1.3.5", "@types/clipboard": "^2.0.7", diff --git a/packages/admin-ui/src/App.tsx b/packages/admin-ui/src/App.tsx index 31e08d116..c13fdbe6a 100644 --- a/packages/admin-ui/src/App.tsx +++ b/packages/admin-ui/src/App.tsx @@ -9,62 +9,63 @@ import Loading from "components/Loading"; import Welcome from "components/welcome/Welcome"; import SideBar from "components/sidebar/SideBar"; import { TopBar } from "components/topbar/TopBar"; +import { rootPath as dashboardRootPath } from "./pages/dashboard"; // Pages import { pages } from "./pages"; import { Login } from "./start-pages/Login"; import { Register } from "./start-pages/Register"; import { NoConnection } from "start-pages/NoConnection"; // Types -import { Theme, UsageMode } from "types"; +import { AppContextIface } from "types"; -export const UsageContext = React.createContext({ - usage: "advanced", - toggleUsage: () => {} -}); - -export const ThemeContext = React.createContext({ +export const AppContext = React.createContext<AppContextIface>({ theme: "light", - toggleTheme: () => {} + stakersModuleStatus: "enabled", + rollupsModuleStatus: "disabled", + toggleTheme: () => {}, + toggleStakersModuleStatus: () => {}, + toggleRollupsModuleStatus: () => {} }); +const useLocalStorage = (key: string, initialValue: string) => { + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + return initialValue; + } + }); + + useEffect(() => { + window.localStorage.setItem(key, JSON.stringify(storedValue)); + }, [key, storedValue]); + + return [storedValue, setStoredValue]; +}; + function MainApp({ username }: { username: string }) { // App is the parent container of any other component. // If this re-renders, the whole app will. So DON'T RERENDER APP! // Check ONCE what is the status of the VPN and redirect to the login page. - const [screenWidth, setScreenWidth] = useState(window.screen.width); - - //const storedUsage = localStorage.getItem("usage"); - const storedTheme = localStorage.getItem("theme"); - //const initialUsage = storedUsage === "advanced" ? "advanced" : "basic"; - const initialUsage = "advanced"; - const initialTheme = - storedTheme === "light" || storedTheme === "dark" ? storedTheme : "light"; - - const [theme, setTheme] = useState<Theme>(initialTheme); - const [usage, setUsage] = useState<UsageMode>(initialUsage); - - const toggleTheme = () => { - setTheme(curr => (curr === "light" ? "dark" : "light")); - }; - - const toggleUsage = () => { - setUsage(curr => (curr === "basic" ? "advanced" : "basic")); - }; - - useEffect(() => { - localStorage.setItem("theme", theme); - }, [theme]); - - useEffect(() => { - localStorage.setItem("usage", usage); - }, [usage]); + const [screenWidth, setScreenWidth] = useState(window.innerWidth); + const [theme, setTheme] = useLocalStorage("theme", "light"); + const [usage, setUsage] = useLocalStorage("usage", "advanced"); + const [stakersModuleStatus, setStakersModuleStatus] = useLocalStorage( + "stakersModuleStatus", + "enabled" + ); + const [rollupsModuleStatus, setRollupsModuleStatus] = useLocalStorage( + "rollupsModuleStatus", + "disabled" + ); useEffect(() => { const handleResize = () => setScreenWidth(window.innerWidth); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); - }, [screenWidth]); + }, []); // Scroll to top on pathname change const screenLocation = useLocation(); @@ -72,23 +73,62 @@ function MainApp({ username }: { username: string }) { window.scrollTo(0, 0); }, [screenLocation.pathname]); + const contextValue = { + theme, + usage, + stakersModuleStatus, + rollupsModuleStatus, + toggleTheme: () => + setTheme((curr: string) => (curr === "light" ? "dark" : "light")), + toggleUsage: () => + setUsage((curr: string) => (curr === "basic" ? "advanced" : "basic")), + toggleStakersModuleStatus: () => + setStakersModuleStatus((curr: string) => + curr === "enabled" ? "disabled" : "enabled" + ), + toggleRollupsModuleStatus: () => + setRollupsModuleStatus((curr: string) => + curr === "enabled" ? "disabled" : "enabled" + ) + }; + return ( - <UsageContext.Provider value={{ usage, toggleUsage }}> - <ThemeContext.Provider value={{ theme, toggleTheme }}> - <div className="body" id={theme}> - <SideBar screenWidth={screenWidth} /> - <TopBar - username={username} - theme={theme} - toggleUsage={toggleUsage} - toggleTheme={toggleTheme} - /> - <div id="main"> - <ErrorBoundary> - <NotificationsMain /> - </ErrorBoundary> - <Routes> - {Object.values(pages).map(({ RootComponent, rootPath }) => ( + <AppContext.Provider value={contextValue}> + <div className="body" id={theme}> + <SideBar screenWidth={screenWidth} /> + <TopBar + username={username} + theme={theme} + toggleUsage={contextValue.toggleUsage} + toggleTheme={contextValue.toggleTheme} + /> + <div id="main"> + <ErrorBoundary> + <NotificationsMain /> + </ErrorBoundary> + <Routes> + {/** Provide the app context only to the dashboard (where the modules switch is handled) */} + {Object.values(pages).map(({ RootComponent, rootPath }) => + rootPath === dashboardRootPath ? ( + <Route + key={rootPath} + path={rootPath} + element={ + <ErrorBoundary> + <RootComponent + modulesContext={{ + stakersModuleStatus: contextValue.stakersModuleStatus, + rollupsModuleStatus: contextValue.rollupsModuleStatus, + toggleStakersModuleStatus: + contextValue.toggleStakersModuleStatus, + toggleRollupsModuleStatus: + contextValue.toggleRollupsModuleStatus + }} + /> + </ErrorBoundary> + } + /> + ) : ( <Route key={rootPath} path={rootPath} @@ -98,19 +138,19 @@ function MainApp({ username }: { username: string }) { </ErrorBoundary> } /> - ))} - {/* Redirection for routes with hashes */} - {/* 404 routes redirect to dashboard or default page */} - <Route path="*" element={<DefaultRedirect />} /> - </Routes> - </div> - - {/* Place here non-page components */} - <Welcome /> - <ToastContainer /> + ) + )} + {/* Redirection for routes with hashes */} + {/* 404 routes redirect to dashboard or default page */} + <Route path="*" element={<DefaultRedirect />} /> + </Routes> </div> - </ThemeContext.Provider> - </UsageContext.Provider> + + {/* Place here non-page components */} + <Welcome /> + <ToastContainer /> + </div> + </AppContext.Provider> ); } diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index ed55e15dc..438340e01 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -223,11 +223,11 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = { ethProvider: "http://geth.dappnode:8545", fullnodeDomainTarget: "geth.dnp.dappnode.eth", newFeatureIds: [ - "repository", - "repository-fallback", - "system-auto-updates", - "enable-ethical-metrics", - "change-host-password" + //"repository", + //"repository-fallback", + //"system-auto-updates", + //"enable-ethical-metrics", + //"change-host-password" ] }), natRenewalEnable: async () => {}, diff --git a/packages/admin-ui/src/components/sidebar/SideBar.tsx b/packages/admin-ui/src/components/sidebar/SideBar.tsx index 903b6f6be..b465ce194 100644 --- a/packages/admin-ui/src/components/sidebar/SideBar.tsx +++ b/packages/admin-ui/src/components/sidebar/SideBar.tsx @@ -1,23 +1,26 @@ import React from "react"; import { NavLink } from "react-router-dom"; -import { advancedItems, basicItems, fundedBy } from "./navbarItems"; +import { sidenavItems, fundedBy } from "./navbarItems"; import logoWide from "img/dappnode-logo-wide-min.png"; import logoWideDark from "img/dappnode-logo-wide-min-dark.png"; import logomin from "img/dappnode-logo-only.png"; -import { ThemeContext, UsageContext } from "../../App"; +import { AppContext } from "../../App"; import "./sidebar.scss"; -if (!Array.isArray(advancedItems)) - throw Error("advancedItems must be an array"); -if (!Array.isArray(basicItems)) throw Error("basicItems must be an array"); +if (!Array.isArray(sidenavItems)) throw Error("sidenavItems must be an array"); if (!Array.isArray(fundedBy)) throw Error("fundedBy must be an array"); export default function SideBar({ screenWidth }: { screenWidth: number }) { - const { theme } = React.useContext(ThemeContext); - const { usage } = React.useContext(UsageContext); + const { theme, rollupsModuleStatus, stakersModuleStatus } = React.useContext( + AppContext + ); + + const stakersItem = sidenavItems.find(item => item.name === "Stakers"); + if (stakersItem) { + if (stakersModuleStatus === "enabled") stakersItem.show = true; + else stakersItem.show = false; + } - const sidenavItems = - usage === "advanced" ? [...basicItems, ...advancedItems] : basicItems; return ( <div id="sidebar"> <NavLink to={"/"}> @@ -35,19 +38,16 @@ export default function SideBar({ screenWidth }: { screenWidth: number }) { </NavLink> <div className="nav"> - {sidenavItems.map(item => ( - <NavLink - key={item.name} - className="sidenav-item selectable" - to={item.href} - > - <item.icon /> - {/* 640 px = 40 rem */} - {screenWidth > 640 && ( - <span className="name svg-text">{item.name}</span> - )} - </NavLink> - ))} + {sidenavItems + .filter(item => item.show === true) + .map(item => ( + <NavLink className={`sidenav-item selectable`} to={item.href}> + <item.icon /> + {screenWidth > 640 && ( + <span className="name svg-text">{item.name}</span> + )} + </NavLink> + ))} </div> {/* spacer keeps the funded-by section at the bottom (if possible) */} diff --git a/packages/admin-ui/src/components/sidebar/sidebar.scss b/packages/admin-ui/src/components/sidebar/sidebar.scss index a28ad69a5..669ff106c 100644 --- a/packages/admin-ui/src/components/sidebar/sidebar.scss +++ b/packages/admin-ui/src/components/sidebar/sidebar.scss @@ -23,6 +23,28 @@ } } +/* Define the fade transition */ +/* Slide & Fade Transition */ + +/* Slide & Fade Transition for nav-item */ + +.nav-item-enter, +.nav-item-exit-active { + opacity: 0; + transform: translateX(-100%); +} + +.nav-item-enter-active, +.nav-item-exit { + opacity: 1; + transform: translateX(0); +} + +.nav-item-enter-active, +.nav-item-exit-active { + transition: opacity 500ms ease-in, transform 500ms ease-in-out; +} + /* * ============ * Sidenav-item diff --git a/packages/admin-ui/src/components/topbar/dropdownMenus/ThemeSwitch.tsx b/packages/admin-ui/src/components/topbar/dropdownMenus/ThemeSwitch.tsx index 89617318c..3205845cf 100644 --- a/packages/admin-ui/src/components/topbar/dropdownMenus/ThemeSwitch.tsx +++ b/packages/admin-ui/src/components/topbar/dropdownMenus/ThemeSwitch.tsx @@ -2,7 +2,7 @@ import { BsMoon } from "react-icons/bs"; import { FaSun } from "react-icons/fa"; import React from "react"; import "./dropdown.scss"; -import { ThemeContext } from "App"; +import { AppContext } from "App"; import Tooltip from "react-bootstrap/Tooltip"; import OverlayTrigger from "react-bootstrap/OverlayTrigger"; @@ -11,7 +11,7 @@ export default function ThemeSwitch({ }: { toggleTheme: () => void; }) { - const { theme } = React.useContext(ThemeContext); + const { theme } = React.useContext(AppContext); return ( <div className="tn-dropdown"> <OverlayTrigger diff --git a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx index c35e17ecd..d5906bd41 100644 --- a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx @@ -7,26 +7,39 @@ import { PackageUpdates } from "./PackageUpdates"; // Components import SubTitle from "components/SubTitle"; import Title from "components/Title"; - -import "./dashboard.scss"; import Modules from "./Modules"; - -export default function Dashboard() { +import "./dashboard.scss"; +import { ModulesContext } from "types"; +export default function Dashboard({ + modulesContext +}: { + modulesContext?: ModulesContext; +}) { return ( <> <Title title={title} /> - <SubTitle>Package updates</SubTitle> - <PackageUpdates /> + <div className="dashboard-layout"> + <div className="dashboard-right"> + {modulesContext && ( + <> + <SubTitle>Modules</SubTitle> + <Modules modulesContext={modulesContext} />{" "} + </> + )} - <SubTitle>Modules</SubTitle> - <Modules /> + <SubTitle>Package updates</SubTitle> + <PackageUpdates /> + </div> - <SubTitle>Chains</SubTitle> - <ChainCards /> + <div className="dashboard-left"> + <SubTitle>Chains</SubTitle> + <ChainCards /> - <SubTitle>Machine stats</SubTitle> - <HostStats /> + <SubTitle>Machine stats</SubTitle> + <HostStats /> + </div> + </div> </> ); } diff --git a/packages/admin-ui/src/pages/dashboard/components/Modules.tsx b/packages/admin-ui/src/pages/dashboard/components/Modules.tsx index 2c12c9d42..353c28295 100644 --- a/packages/admin-ui/src/pages/dashboard/components/Modules.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/Modules.tsx @@ -1,5 +1,61 @@ import React from "react"; +import { ModulesContext } from "types"; +import Card from "components/Card"; +import Switch from "components/Switch"; +import { HelpTo } from "components/Help"; +import { docsUrl } from "params"; -export default function Modules() { - return <div>Modules</div>; +export default function Modules({ + modulesContext +}: { + modulesContext: ModulesContext; +}) { + const { + stakersModuleStatus, + rollupsModuleStatus, + toggleStakersModuleStatus, + toggleRollupsModuleStatus + } = modulesContext; + + return ( + <div className="dashboard-cards"> + <ModuleCard + name="Stakers" + help={docsUrl.stakers} + checked={stakersModuleStatus === "enabled"} + onToggle={toggleStakersModuleStatus} + /> + + <ModuleCard + name="Rollups" + help={docsUrl.rollups} + checked={rollupsModuleStatus === "enabled"} + onToggle={toggleRollupsModuleStatus} + /> + </div> + ); +} + +function ModuleCard({ + name, + help, + checked, + onToggle +}: { + name: string; + help: string; + checked: boolean; + onToggle: () => void; +}) { + return ( + <Card className="module-card"> + <div className="name"> + <span className="text">{name}</span> + {help && <HelpTo url={help} />} + </div> + <div className="switch"> + <Switch checked={checked} onToggle={onToggle} /> + </div> + </Card> + ); } diff --git a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss index a570a2b3f..e1bf42f89 100644 --- a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss +++ b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss @@ -1,3 +1,23 @@ +.dashboard-layout { + display: grid; + grid-gap: var(--default-spacing); + grid-template-columns: 60% auto; + grid-template-rows: auto; + grid-template-areas: "left right"; + + @media screen and (max-width: 65rem) { + display: block; + } +} + +.dashboard-right { + grid-area: right; +} + +.dashboard-left { + grid-area: left; +} + .dashboard-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); @@ -13,6 +33,17 @@ } } +/* Module card */ +.module-card { + display: flex; + justify-content: space-between; + align-items: center; + + .text { + margin-right: 3px; + } +} + /* Chain cards and Stats cards */ .chain-card .name, diff --git a/packages/admin-ui/src/params.ts b/packages/admin-ui/src/params.ts index 7e6a849d0..7de9da61f 100755 --- a/packages/admin-ui/src/params.ts +++ b/packages/admin-ui/src/params.ts @@ -89,7 +89,9 @@ export const docsUrl = { httpsExplanation: "https://docs.dappnode.io/user/product-manual/system#networkk", ipfsPeersExplanation: - "https://docs.dappnode.io/user/product-manual/system#peers" + "https://docs.dappnode.io/user/product-manual/system#peers", + stakers: "https://docs.dappnode.io/docs/user/staking/overview", + rollups: "https://docs.dappnode.io/docs/user/rollups/overview" }; export const forumUrl = { diff --git a/packages/admin-ui/src/types.ts b/packages/admin-ui/src/types.ts index 6cd817768..5d32d829c 100644 --- a/packages/admin-ui/src/types.ts +++ b/packages/admin-ui/src/types.ts @@ -47,3 +47,22 @@ export interface DappnodeParams { export type Theme = "light" | "dark"; export type UsageMode = "basic" | "advanced"; + +export type UiModuleStatus = "enabled" | "disabled"; + +export interface AppContextIface { + theme: Theme; + stakersModuleStatus: UiModuleStatus; + rollupsModuleStatus: UiModuleStatus; + toggleTheme: () => void; + toggleStakersModuleStatus: () => void; + toggleRollupsModuleStatus: () => void; +} + +export type ModulesContext = Pick< + AppContextIface, + | "stakersModuleStatus" + | "rollupsModuleStatus" + | "toggleStakersModuleStatus" + | "toggleRollupsModuleStatus" +>; From 9157abf4d486a63e316e8d46f4d5fac647d84693 Mon Sep 17 00:00:00 2001 From: dsimog01 <dsimog01@estudiantes.unileon.es> Date: Wed, 20 Sep 2023 15:01:05 +0000 Subject: [PATCH 31/45] Update development hashes --- .../dappmanager/src/modules/optimism/getOptimismConfig.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index 6d62937c9..f0541cd6e 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -33,9 +33,9 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { let hash = ""; if (execClient === "op-geth.dnp.dappnode.eth") { - hash = "/ipfs/QmURvP2uKPKyBFpkwFE8JstB8eDcQ2Xd9PmW9TMCPPKpSL"; + hash = "/ipfs/QmPyzdBbgpfccVFCiGh9kHsmbZe4pM1d4KdP6WAJED4TX9"; } else { - hash = "/ipfs/QmcdDtv2n3ottwn3tvpMiBbLfVMSp1KCHzTzjyLRL6PdqL"; + hash = "/ipfs/QmZW23WHWePEMe1o8NDedgoMzyWv4Jw6RbbZ4hACUCjozM"; } const pkgData = await getPkgData(releaseFetcher, hash); @@ -67,7 +67,7 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { /**if (!(await releaseFetcher.repoExists(optimismNode))) throw Error(`Repository ${optimismNode} does not exist`);*/ - const hash = "/ipfs/QmT7tBCdPX6HLQXmQynuETkwAPCSeAqFbANVKaFJnnQ6nw"; + const hash = "/ipfs/QmVJV1ZbDyTNDReeNu6ykQfUXZ4SKXmLFiqULQ3S1JYcXG"; const pkgData = await getPkgData(releaseFetcher, hash); const mainnetRpcUrl = getOptimismNodeRpcUrl(); From 62460306d0080b93df06b334eede1b45b63c94d8 Mon Sep 17 00:00:00 2001 From: dappnodedev <144998261+dappnodedev@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:27:18 +0200 Subject: [PATCH 32/45] Add OP community (#1604) --- .../pages/rollups/components/OpCommunity.tsx | 70 +++++++++++++++++++ .../src/pages/rollups/components/Optimism.tsx | 2 + .../pages/rollups/components/opCommunity.css | 13 ++++ 3 files changed, 85 insertions(+) create mode 100644 packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx create mode 100644 packages/admin-ui/src/pages/rollups/components/opCommunity.css diff --git a/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx b/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx new file mode 100644 index 000000000..ce82365d8 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx @@ -0,0 +1,70 @@ +import React, { useState } from "react"; +import { Accordion, Card } from "react-bootstrap"; +import { IoIosArrowDropdown, IoIosArrowDropup } from "react-icons/io"; +import { BsDiscord } from "react-icons/bs"; +import { MdForum, MdHowToVote } from "react-icons/md"; +import "./opCommunity.css"; + +export default function OpCommunity() { + const [isOpen, setIsOpen] = useState(false); + + const iconStyle = { + fontSize: "2rem", + marginRight: "1rem" + }; + + return ( + <div> + <Accordion defaultActiveKey={isOpen ? "0" : ""}> + <Card> + <Accordion.Toggle + as={Card.Header} + eventKey="0" + onClick={() => setIsOpen(!isOpen)} + style={{ cursor: "pointer" }} + > + {isOpen ? <IoIosArrowDropup /> : <IoIosArrowDropdown />}{" "} + <b>Optimism Community</b> + </Accordion.Toggle> + <Accordion.Collapse eventKey="0"> + <Card.Body> + <div style={{ marginBottom: "1rem" }}> + Dive into the next-gen blockchain scalability solution. Optimism + revolutionizes transaction speeds, ensuring a seamless and + efficient decentralized experience. Be part of the movement + that's shaping the future of blockchain. + </div> + + <div> + <a href="https://discord.gg/optimism" className="iconLink"> + <BsDiscord style={iconStyle} /> + </a> + <b>Discord</b>: Connect with fellow enthusiasts, share insights, + seek assistance, or simply engage in lively blockchain + discussions. + </div> + <div> + <a href="https://gov.optimism.io/" className="iconLink"> + <MdForum style={iconStyle} /> + </a> + <b>Governance Forum</b>: Delve into deep-dive discussions, share + your perspectives, or keep abreast with the latest updates. + </div> + <div> + <a + href="https://snapshot.org/#/opcollective.eth" + className="iconLink" + > + <MdHowToVote style={iconStyle} /> + </a> + <b>Snapshot Governance Voting</b>: Have a say in the future of + Optimism. Voice your opinion and cast your vote on key + proposals. + </div> + </Card.Body> + </Accordion.Collapse> + </Card> + </Accordion> + </div> + ); +} diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index 38bac1d3c..456a6992f 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -17,6 +17,7 @@ import Input from "components/Input"; import { confirm } from "components/ConfirmDialog"; import { disclaimer } from "../data"; import { withToast } from "components/toast/Toast"; +import OpCommunity from "./OpCommunity"; export default function Optimism({ description }: { description: string }) { const { theme } = React.useContext(ThemeContext); @@ -109,6 +110,7 @@ export default function Optimism({ description }: { description: string }) { return ( <div className={theme === "light" ? "optimism-light" : "optimism-dark"}> + <OpCommunity /> {currentOptimismConfigReq.data ? ( <Card> <p> diff --git a/packages/admin-ui/src/pages/rollups/components/opCommunity.css b/packages/admin-ui/src/pages/rollups/components/opCommunity.css new file mode 100644 index 000000000..ea258d479 --- /dev/null +++ b/packages/admin-ui/src/pages/rollups/components/opCommunity.css @@ -0,0 +1,13 @@ +.iconLink { + color: inherit; /* inherit color to make sure it looks like regular text */ + text-decoration: none; /* to remove the underline */ +} + +.iconLink:hover { + color: var(--dappnode-links-color); /* change the color on hover */ +} + +.iconLink svg { + /* targeting the svg which is the icon */ + vertical-align: middle; /* to align the icon with the text */ +} From 40deb79541b8b17c9062859e7271617c39d1f035 Mon Sep 17 00:00:00 2001 From: dappnodedev <diego@dappnode.io> Date: Thu, 21 Sep 2023 13:23:41 +0000 Subject: [PATCH 33/45] Update dropdown icon --- .../admin-ui/src/pages/rollups/components/OpCommunity.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx b/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx index ce82365d8..2f26c84b7 100644 --- a/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx +++ b/packages/admin-ui/src/pages/rollups/components/OpCommunity.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Accordion, Card } from "react-bootstrap"; -import { IoIosArrowDropdown, IoIosArrowDropup } from "react-icons/io"; +import { IoIosArrowDown, IoIosArrowUp } from "react-icons/io"; import { BsDiscord } from "react-icons/bs"; import { MdForum, MdHowToVote } from "react-icons/md"; import "./opCommunity.css"; @@ -23,7 +23,7 @@ export default function OpCommunity() { onClick={() => setIsOpen(!isOpen)} style={{ cursor: "pointer" }} > - {isOpen ? <IoIosArrowDropup /> : <IoIosArrowDropdown />}{" "} + {isOpen ? <IoIosArrowUp /> : <IoIosArrowDown />}{" "} <b>Optimism Community</b> </Accordion.Toggle> <Accordion.Collapse eventKey="0"> From d030bc39a338b0b084d8de83d7b4844880c25833 Mon Sep 17 00:00:00 2001 From: dappnodedev <diego@dappnode.io> Date: Thu, 21 Sep 2023 14:31:09 +0000 Subject: [PATCH 34/45] Update Op instructions --- .../src/pages/rollups/components/Optimism.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index 456a6992f..ab2ccb4f7 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -114,11 +114,14 @@ export default function Optimism({ description }: { description: string }) { {currentOptimismConfigReq.data ? ( <Card> <p> - Set up your Optimism node configuration. You will need to: <br /> - (1) Choose an Execution Layer client <br /> - (2) Install the optimism node with an Ethereum RPC endpoint + Set up your Optimism node configuration: <br /> + (1) <b>Choose</b> an <b>Execution Client</b> <br /> + (2) <b>Select</b> the <b>Optimism Node</b> <br /> + (3) <b>Input</b> the <b>Ethereum RPC URL</b> (Not necessary if you + are already running an Ethereum mainnet node on this Dappnode) <br /> - (3) Optional; activate/deactivate archive node for historical tx. + (4) [Optional] <b>Select Legacy Geth</b> to enable historical + transactions </p> <br /> From 04abfc31bbba06221940105d8e456c53e43b86a2 Mon Sep 17 00:00:00 2001 From: dappnodedev <diego@dappnode.io> Date: Thu, 21 Sep 2023 14:31:21 +0000 Subject: [PATCH 35/45] Update op-erigon --- packages/dappmanager/src/modules/optimism/getOptimismConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index f0541cd6e..38cb61335 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -35,7 +35,7 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { if (execClient === "op-geth.dnp.dappnode.eth") { hash = "/ipfs/QmPyzdBbgpfccVFCiGh9kHsmbZe4pM1d4KdP6WAJED4TX9"; } else { - hash = "/ipfs/QmZW23WHWePEMe1o8NDedgoMzyWv4Jw6RbbZ4hACUCjozM"; + hash = "/ipfs/QmcPhoAV9idcHUd3aifwHrvYv3v8695Hwenkys5rE6PB8v"; } const pkgData = await getPkgData(releaseFetcher, hash); From 6c11cd00038e81decb11d2bb5536d51003912ebb Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Thu, 21 Sep 2023 19:18:36 +0200 Subject: [PATCH 36/45] add UIX to modules dropdown --- packages/admin-ui/src/App.tsx | 59 +++++------------- .../admin-ui/src/components/topbar/TopBar.tsx | 18 +++--- .../topbar/dropdownMenus/BaseDropdown.tsx | 17 +++++- .../topbar/dropdownMenus/Modules.tsx | 44 +++++++++++++ .../topbar/dropdownMenus/dropdown.scss | 11 +++- .../pages/dashboard/components/Dashboard.tsx | 16 +---- .../pages/dashboard/components/Modules.tsx | 61 ------------------- 7 files changed, 92 insertions(+), 134 deletions(-) create mode 100644 packages/admin-ui/src/components/topbar/dropdownMenus/Modules.tsx delete mode 100644 packages/admin-ui/src/pages/dashboard/components/Modules.tsx diff --git a/packages/admin-ui/src/App.tsx b/packages/admin-ui/src/App.tsx index c13fdbe6a..9bd0b1b82 100644 --- a/packages/admin-ui/src/App.tsx +++ b/packages/admin-ui/src/App.tsx @@ -9,7 +9,6 @@ import Loading from "components/Loading"; import Welcome from "components/welcome/Welcome"; import SideBar from "components/sidebar/SideBar"; import { TopBar } from "components/topbar/TopBar"; -import { rootPath as dashboardRootPath } from "./pages/dashboard"; // Pages import { pages } from "./pages"; import { Login } from "./start-pages/Login"; @@ -51,7 +50,6 @@ function MainApp({ username }: { username: string }) { const [screenWidth, setScreenWidth] = useState(window.innerWidth); const [theme, setTheme] = useLocalStorage("theme", "light"); - const [usage, setUsage] = useLocalStorage("usage", "advanced"); const [stakersModuleStatus, setStakersModuleStatus] = useLocalStorage( "stakersModuleStatus", "enabled" @@ -73,15 +71,12 @@ function MainApp({ username }: { username: string }) { window.scrollTo(0, 0); }, [screenLocation.pathname]); - const contextValue = { + const appContext = { theme, - usage, stakersModuleStatus, rollupsModuleStatus, toggleTheme: () => setTheme((curr: string) => (curr === "light" ? "dark" : "light")), - toggleUsage: () => - setUsage((curr: string) => (curr === "basic" ? "advanced" : "basic")), toggleStakersModuleStatus: () => setStakersModuleStatus((curr: string) => curr === "enabled" ? "disabled" : "enabled" @@ -93,53 +88,27 @@ function MainApp({ username }: { username: string }) { }; return ( - <AppContext.Provider value={contextValue}> + <AppContext.Provider value={appContext}> <div className="body" id={theme}> <SideBar screenWidth={screenWidth} /> - <TopBar - username={username} - theme={theme} - toggleUsage={contextValue.toggleUsage} - toggleTheme={contextValue.toggleTheme} - /> + <TopBar username={username} appContext={appContext} /> <div id="main"> <ErrorBoundary> <NotificationsMain /> </ErrorBoundary> <Routes> {/** Provide the app context only to the dashboard (where the modules switch is handled) */} - {Object.values(pages).map(({ RootComponent, rootPath }) => - rootPath === dashboardRootPath ? ( - <Route - key={rootPath} - path={rootPath} - element={ - <ErrorBoundary> - <RootComponent - modulesContext={{ - stakersModuleStatus: contextValue.stakersModuleStatus, - rollupsModuleStatus: contextValue.rollupsModuleStatus, - toggleStakersModuleStatus: - contextValue.toggleStakersModuleStatus, - toggleRollupsModuleStatus: - contextValue.toggleRollupsModuleStatus - }} - /> - </ErrorBoundary> - } - /> - ) : ( - <Route - key={rootPath} - path={rootPath} - element={ - <ErrorBoundary> - <RootComponent /> - </ErrorBoundary> - } - /> - ) - )} + {Object.values(pages).map(({ RootComponent, rootPath }) => ( + <Route + key={rootPath} + path={rootPath} + element={ + <ErrorBoundary> + <RootComponent /> + </ErrorBoundary> + } + /> + ))} {/* Redirection for routes with hashes */} {/* 404 routes redirect to dashboard or default page */} <Route path="*" element={<DefaultRedirect />} /> diff --git a/packages/admin-ui/src/components/topbar/TopBar.tsx b/packages/admin-ui/src/components/topbar/TopBar.tsx index fe2250f70..dc797b9f5 100644 --- a/packages/admin-ui/src/components/topbar/TopBar.tsx +++ b/packages/admin-ui/src/components/topbar/TopBar.tsx @@ -10,35 +10,33 @@ import "./topbar.scss"; import "./notifications.scss"; // import UsageSwitch from "./dropdownMenus/UsageSwitch"; // Types -import { Theme } from "types"; +import { AppContextIface } from "types"; +import Modules from "./dropdownMenus/Modules"; export const TopBar = ({ username, - theme, - toggleTheme, - toggleUsage + appContext }: { username: string; - theme: Theme; - toggleTheme: () => void; - toggleUsage: () => void; + appContext: AppContextIface; }) => ( <div id="topbar"> {/* Right justified items */} - {theme === "light" ? ( + {appContext.theme === "light" ? ( <div className="beta"> <span>BETA</span> {/* Theme usage requires more feedback */} {/*<UsageSwitch toggleUsage={toggleUsage} /> */} - <ThemeSwitch toggleTheme={toggleTheme} /> + <ThemeSwitch toggleTheme={appContext.toggleTheme} /> </div> ) : ( - <ThemeSwitch toggleTheme={toggleTheme} /> + <ThemeSwitch toggleTheme={appContext.toggleTheme} /> )} <DappnodeIdentity /> <div className="topnav-icon-separator" /> + <Modules modulesContext={appContext} /> <ChainDataDropdown /> <Notifications /> <Profile username={username} /> diff --git a/packages/admin-ui/src/components/topbar/dropdownMenus/BaseDropdown.tsx b/packages/admin-ui/src/components/topbar/dropdownMenus/BaseDropdown.tsx index 07cb120e0..a4e6e8cbc 100644 --- a/packages/admin-ui/src/components/topbar/dropdownMenus/BaseDropdown.tsx +++ b/packages/admin-ui/src/components/topbar/dropdownMenus/BaseDropdown.tsx @@ -3,6 +3,7 @@ import ProgressBar from "react-bootstrap/ProgressBar"; import RenderMarkdown from "components/RenderMarkdown"; import "./dropdown.scss"; import { HelpTo } from "components/Help"; +import Switch from "components/Switch"; // Bubble color does not support "info", nor "light". So "light" with display nothing type BubbleColor = "danger" | "warning" | "success" | "light"; @@ -44,6 +45,10 @@ type MessageType = "danger" | "warning" | "success" | "info"; export interface BaseDropdownMessage { type?: MessageType; title?: string | JSX.Element | null; + toggle?: { + checked: boolean; + onToggle: () => void; + }; body?: string; help?: string; // href link to attach to help icon progress?: number; @@ -143,12 +148,20 @@ function BaseDropdown({ <div className={`menu ${collapsed ? "" : "show"}`}> <div className="header">{name}</div> {messages.map( - ({ type, title, body, progress, showProgress, help }, i) => ( + ({ type, title, toggle, body, progress, showProgress, help }, i) => ( <div key={i}> {title && ( <div className="title"> <span className={`text text-${type}`}>{title}</span> - {help && <HelpTo url={help} />} + <div className="title-actions"> + {help && <HelpTo url={help} />} + {toggle && ( + <Switch + checked={toggle.checked} + onToggle={toggle.onToggle} + /> + )} + </div> </div> )} diff --git a/packages/admin-ui/src/components/topbar/dropdownMenus/Modules.tsx b/packages/admin-ui/src/components/topbar/dropdownMenus/Modules.tsx new file mode 100644 index 000000000..8c7a729a6 --- /dev/null +++ b/packages/admin-ui/src/components/topbar/dropdownMenus/Modules.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { ModulesContext } from "types"; +import { docsUrl } from "params"; +import BaseDropdown from "./BaseDropdown"; +import { RiSoundModuleFill } from "react-icons/ri"; + +export default function Modules({ + modulesContext +}: { + modulesContext: ModulesContext; +}) { + const { + stakersModuleStatus, + rollupsModuleStatus, + toggleStakersModuleStatus, + toggleRollupsModuleStatus + } = modulesContext; + + return ( + <BaseDropdown + name="Modules" + messages={[ + { + title: "Stakers", + help: docsUrl.stakers, + toggle: { + checked: stakersModuleStatus === "enabled", + onToggle: toggleStakersModuleStatus + } + }, + { + title: "Rollups", + help: docsUrl.rollups, + toggle: { + checked: rollupsModuleStatus === "enabled", + onToggle: toggleRollupsModuleStatus + } + } + ]} + Icon={RiSoundModuleFill} + className={"modules"} + /> + ); +} diff --git a/packages/admin-ui/src/components/topbar/dropdownMenus/dropdown.scss b/packages/admin-ui/src/components/topbar/dropdownMenus/dropdown.scss index 80ae52edf..19b32f626 100644 --- a/packages/admin-ui/src/components/topbar/dropdownMenus/dropdown.scss +++ b/packages/admin-ui/src/components/topbar/dropdownMenus/dropdown.scss @@ -56,9 +56,13 @@ .text { flex: auto; } - .help { - font-size: 140%; + .title-actions { display: flex; + .help { + font-size: 140%; + display: flex; + margin-right: 10px; + } } } } @@ -90,6 +94,9 @@ &.dappnodeidentity > .menu { right: -9.2rem; } + &.modules > .menu { + right: -8rem; + } // Regulate size of toggle icons &.notifications .tn-dropdown-toggle svg { diff --git a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx index d5906bd41..d79d5e05e 100644 --- a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx @@ -7,27 +7,15 @@ import { PackageUpdates } from "./PackageUpdates"; // Components import SubTitle from "components/SubTitle"; import Title from "components/Title"; -import Modules from "./Modules"; import "./dashboard.scss"; -import { ModulesContext } from "types"; -export default function Dashboard({ - modulesContext -}: { - modulesContext?: ModulesContext; -}) { + +export default function Dashboard() { return ( <> <Title title={title} /> <div className="dashboard-layout"> <div className="dashboard-right"> - {modulesContext && ( - <> - <SubTitle>Modules</SubTitle> - <Modules modulesContext={modulesContext} />{" "} - </> - )} - <SubTitle>Package updates</SubTitle> <PackageUpdates /> </div> diff --git a/packages/admin-ui/src/pages/dashboard/components/Modules.tsx b/packages/admin-ui/src/pages/dashboard/components/Modules.tsx deleted file mode 100644 index 353c28295..000000000 --- a/packages/admin-ui/src/pages/dashboard/components/Modules.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { ModulesContext } from "types"; -import Card from "components/Card"; -import Switch from "components/Switch"; -import { HelpTo } from "components/Help"; -import { docsUrl } from "params"; - -export default function Modules({ - modulesContext -}: { - modulesContext: ModulesContext; -}) { - const { - stakersModuleStatus, - rollupsModuleStatus, - toggleStakersModuleStatus, - toggleRollupsModuleStatus - } = modulesContext; - - return ( - <div className="dashboard-cards"> - <ModuleCard - name="Stakers" - help={docsUrl.stakers} - checked={stakersModuleStatus === "enabled"} - onToggle={toggleStakersModuleStatus} - /> - - <ModuleCard - name="Rollups" - help={docsUrl.rollups} - checked={rollupsModuleStatus === "enabled"} - onToggle={toggleRollupsModuleStatus} - /> - </div> - ); -} - -function ModuleCard({ - name, - help, - checked, - onToggle -}: { - name: string; - help: string; - checked: boolean; - onToggle: () => void; -}) { - return ( - <Card className="module-card"> - <div className="name"> - <span className="text">{name}</span> - {help && <HelpTo url={help} />} - </div> - <div className="switch"> - <Switch checked={checked} onToggle={onToggle} /> - </div> - </Card> - ); -} From 7e22470b01f2f619bb68e00f58bec13f697a4074 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Thu, 21 Sep 2023 23:16:44 +0200 Subject: [PATCH 37/45] add linter disable until rollups tab --- packages/admin-ui/src/components/sidebar/SideBar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/admin-ui/src/components/sidebar/SideBar.tsx b/packages/admin-ui/src/components/sidebar/SideBar.tsx index b465ce194..7fd52b20c 100644 --- a/packages/admin-ui/src/components/sidebar/SideBar.tsx +++ b/packages/admin-ui/src/components/sidebar/SideBar.tsx @@ -11,6 +11,7 @@ if (!Array.isArray(sidenavItems)) throw Error("sidenavItems must be an array"); if (!Array.isArray(fundedBy)) throw Error("fundedBy must be an array"); export default function SideBar({ screenWidth }: { screenWidth: number }) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { theme, rollupsModuleStatus, stakersModuleStatus } = React.useContext( AppContext ); From 4b67218ea57823a7d0bac54cbfd96133f5f97ede Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 08:56:15 +0200 Subject: [PATCH 38/45] add eventbus import --- packages/dappmanager/src/utils/getPkgItemData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dappmanager/src/utils/getPkgItemData.ts b/packages/dappmanager/src/utils/getPkgItemData.ts index 9d59411aa..5c81b19eb 100644 --- a/packages/dappmanager/src/utils/getPkgItemData.ts +++ b/packages/dappmanager/src/utils/getPkgItemData.ts @@ -1,9 +1,9 @@ import { PackageItemData, PackageRelease } from "@dappnode/common"; -import { eventBus } from "../eventBus.js"; import { ReleaseFetcher } from "../modules/release/index.js"; import * as db from "../db/index.js"; import { pick } from "lodash-es"; import { Manifest } from "@dappnode/types"; +import { eventBus } from "@dappnode/eventbus"; export async function getPkgData( releaseFetcher: ReleaseFetcher, From 54a371d33e8c3051bb4502e38c7456fdabecb8be Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 08:59:13 +0200 Subject: [PATCH 39/45] add eventbus import --- packages/dappmanager/src/utils/getPkgItemData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dappmanager/src/utils/getPkgItemData.ts b/packages/dappmanager/src/utils/getPkgItemData.ts index 9d59411aa..5c81b19eb 100644 --- a/packages/dappmanager/src/utils/getPkgItemData.ts +++ b/packages/dappmanager/src/utils/getPkgItemData.ts @@ -1,9 +1,9 @@ import { PackageItemData, PackageRelease } from "@dappnode/common"; -import { eventBus } from "../eventBus.js"; import { ReleaseFetcher } from "../modules/release/index.js"; import * as db from "../db/index.js"; import { pick } from "lodash-es"; import { Manifest } from "@dappnode/types"; +import { eventBus } from "@dappnode/eventbus"; export async function getPkgData( releaseFetcher: ReleaseFetcher, From c134ad0b0a3fffe9b0eb8ac9c4ac26daa453eea5 Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 09:03:18 +0200 Subject: [PATCH 40/45] add rollupsmodule status value --- packages/admin-ui/src/components/sidebar/SideBar.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/admin-ui/src/components/sidebar/SideBar.tsx b/packages/admin-ui/src/components/sidebar/SideBar.tsx index 7fd52b20c..3b4f2a769 100644 --- a/packages/admin-ui/src/components/sidebar/SideBar.tsx +++ b/packages/admin-ui/src/components/sidebar/SideBar.tsx @@ -11,7 +11,6 @@ if (!Array.isArray(sidenavItems)) throw Error("sidenavItems must be an array"); if (!Array.isArray(fundedBy)) throw Error("fundedBy must be an array"); export default function SideBar({ screenWidth }: { screenWidth: number }) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { theme, rollupsModuleStatus, stakersModuleStatus } = React.useContext( AppContext ); @@ -21,6 +20,11 @@ export default function SideBar({ screenWidth }: { screenWidth: number }) { if (stakersModuleStatus === "enabled") stakersItem.show = true; else stakersItem.show = false; } + const rollupsItem = sidenavItems.find(item => item.name === "Rollups"); + if (rollupsItem) { + if (rollupsModuleStatus === "enabled") rollupsItem.show = true; + else rollupsItem.show = false; + } return ( <div id="sidebar"> From 9fb0bd7d96547ef027c66ad4c9e7cecf9bfc6756 Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 09:08:31 +0200 Subject: [PATCH 41/45] fix app context import --- packages/admin-ui/src/pages/rollups/components/Optimism.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx index ab2ccb4f7..37bff5b78 100644 --- a/packages/admin-ui/src/pages/rollups/components/Optimism.tsx +++ b/packages/admin-ui/src/pages/rollups/components/Optimism.tsx @@ -1,6 +1,6 @@ import React from "react"; import { api, useApi } from "api"; -import { ThemeContext } from "App"; +import { AppContext } from "App"; import Card from "components/Card"; import ErrorView from "components/ErrorView"; import Loading from "components/Loading"; @@ -20,7 +20,7 @@ import { withToast } from "components/toast/Toast"; import OpCommunity from "./OpCommunity"; export default function Optimism({ description }: { description: string }) { - const { theme } = React.useContext(ThemeContext); + const { theme } = React.useContext(AppContext); const currentOptimismConfigReq = useApi.optimismConfigGet(); From ee4409753164e27ab2476bd5e5f852edfc5e7ac9 Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 09:10:49 +0200 Subject: [PATCH 42/45] fix navbarIrtems --- packages/admin-ui/src/components/sidebar/navbarItems.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/admin-ui/src/components/sidebar/navbarItems.ts b/packages/admin-ui/src/components/sidebar/navbarItems.ts index 8bbf12981..f754e146a 100644 --- a/packages/admin-ui/src/components/sidebar/navbarItems.ts +++ b/packages/admin-ui/src/components/sidebar/navbarItems.ts @@ -101,7 +101,8 @@ export const sidenavItems: { { name: "Rollups", href: rollupsRelativePath, - icon: GiRolledCloth + icon: GiRolledCloth, + show: true }, { name: "Repository", From 370f2056789325e54fc12e31d0da361f83f8c82a Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 14:01:15 +0200 Subject: [PATCH 43/45] remove unused navnar scss --- .../src/components/sidebar/sidebar.scss | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/admin-ui/src/components/sidebar/sidebar.scss b/packages/admin-ui/src/components/sidebar/sidebar.scss index 669ff106c..a28ad69a5 100644 --- a/packages/admin-ui/src/components/sidebar/sidebar.scss +++ b/packages/admin-ui/src/components/sidebar/sidebar.scss @@ -23,28 +23,6 @@ } } -/* Define the fade transition */ -/* Slide & Fade Transition */ - -/* Slide & Fade Transition for nav-item */ - -.nav-item-enter, -.nav-item-exit-active { - opacity: 0; - transform: translateX(-100%); -} - -.nav-item-enter-active, -.nav-item-exit { - opacity: 1; - transform: translateX(0); -} - -.nav-item-enter-active, -.nav-item-exit-active { - transition: opacity 500ms ease-in, transform 500ms ease-in-out; -} - /* * ============ * Sidenav-item From d26c007eb1e35b716fc90e36fe17bc9a8e369dd8 Mon Sep 17 00:00:00 2001 From: Pablo Mendez <mendez4a@gmail.com> Date: Fri, 22 Sep 2023 15:02:53 +0200 Subject: [PATCH 44/45] Do not use key-value pairs to store storage in brw --- packages/admin-ui/src/App.tsx | 38 ++++++++++++++++++---------------- packages/admin-ui/src/types.ts | 2 -- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/admin-ui/src/App.tsx b/packages/admin-ui/src/App.tsx index 9bd0b1b82..6aa57aaad 100644 --- a/packages/admin-ui/src/App.tsx +++ b/packages/admin-ui/src/App.tsx @@ -15,7 +15,7 @@ import { Login } from "./start-pages/Login"; import { Register } from "./start-pages/Register"; import { NoConnection } from "start-pages/NoConnection"; // Types -import { AppContextIface } from "types"; +import { AppContextIface, Theme, UiModuleStatus } from "types"; export const AppContext = React.createContext<AppContextIface>({ theme: "light", @@ -26,18 +26,22 @@ export const AppContext = React.createContext<AppContextIface>({ toggleRollupsModuleStatus: () => {} }); -const useLocalStorage = (key: string, initialValue: string) => { - const [storedValue, setStoredValue] = useState(() => { +const useLocalStorage = <T extends string>( + key: string, + initialValue: T +): [T, React.Dispatch<React.SetStateAction<T>>] => { + const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); - return item ? JSON.parse(item) : initialValue; + // Assert that either the item or initialValue is of type T + return (item as T) || initialValue; } catch (error) { return initialValue; } }); useEffect(() => { - window.localStorage.setItem(key, JSON.stringify(storedValue)); + window.localStorage.setItem(key, storedValue); }, [key, storedValue]); return [storedValue, setStoredValue]; @@ -49,15 +53,13 @@ function MainApp({ username }: { username: string }) { // Check ONCE what is the status of the VPN and redirect to the login page. const [screenWidth, setScreenWidth] = useState(window.innerWidth); - const [theme, setTheme] = useLocalStorage("theme", "light"); - const [stakersModuleStatus, setStakersModuleStatus] = useLocalStorage( - "stakersModuleStatus", - "enabled" - ); - const [rollupsModuleStatus, setRollupsModuleStatus] = useLocalStorage( - "rollupsModuleStatus", - "disabled" - ); + const [theme, setTheme] = useLocalStorage<Theme>("theme", "light"); + const [stakersModuleStatus, setStakersModuleStatus] = useLocalStorage< + UiModuleStatus + >("stakersModuleStatus", "enabled"); + const [rollupsModuleStatus, setRollupsModuleStatus] = useLocalStorage< + UiModuleStatus + >("rollupsModuleStatus", "disabled"); useEffect(() => { const handleResize = () => setScreenWidth(window.innerWidth); @@ -71,18 +73,18 @@ function MainApp({ username }: { username: string }) { window.scrollTo(0, 0); }, [screenLocation.pathname]); - const appContext = { + const appContext: AppContextIface = { theme, stakersModuleStatus, rollupsModuleStatus, toggleTheme: () => - setTheme((curr: string) => (curr === "light" ? "dark" : "light")), + setTheme((curr: Theme) => (curr === "light" ? "dark" : "light")), toggleStakersModuleStatus: () => - setStakersModuleStatus((curr: string) => + setStakersModuleStatus((curr: UiModuleStatus) => curr === "enabled" ? "disabled" : "enabled" ), toggleRollupsModuleStatus: () => - setRollupsModuleStatus((curr: string) => + setRollupsModuleStatus((curr: UiModuleStatus) => curr === "enabled" ? "disabled" : "enabled" ) }; diff --git a/packages/admin-ui/src/types.ts b/packages/admin-ui/src/types.ts index 5d32d829c..388d30654 100644 --- a/packages/admin-ui/src/types.ts +++ b/packages/admin-ui/src/types.ts @@ -46,8 +46,6 @@ export interface DappnodeParams { export type Theme = "light" | "dark"; -export type UsageMode = "basic" | "advanced"; - export type UiModuleStatus = "enabled" | "disabled"; export interface AppContextIface { From 1b6743ec1cd87da6fe7fee35aa7a42c0c423c5e4 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <mendez4a@gmail.com> Date: Mon, 25 Sep 2023 10:07:25 +0200 Subject: [PATCH 45/45] remove hashes --- .../src/modules/optimism/getOptimismConfig.ts | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts index 38cb61335..355325844 100644 --- a/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts +++ b/packages/dappmanager/src/modules/optimism/getOptimismConfig.ts @@ -28,17 +28,10 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { executionClients: await Promise.all( executionClientsOptimism.map(async execClient => { try { - /**if (!(await releaseFetcher.repoExists(execClient))) - throw Error(`Repository ${execClient} does not exist`);*/ + if (!(await releaseFetcher.repoExists(execClient))) + throw Error(`Repository ${execClient} does not exist`); - let hash = ""; - if (execClient === "op-geth.dnp.dappnode.eth") { - hash = "/ipfs/QmPyzdBbgpfccVFCiGh9kHsmbZe4pM1d4KdP6WAJED4TX9"; - } else { - hash = "/ipfs/QmcPhoAV9idcHUd3aifwHrvYv3v8695Hwenkys5rE6PB8v"; - } - - const pkgData = await getPkgData(releaseFetcher, hash); + const pkgData = await getPkgData(releaseFetcher, execClient); return { status: "ok", @@ -64,12 +57,10 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { rollup: await new Promise<OptimismItem<"rollup">>(resolve => { (async () => { try { - /**if (!(await releaseFetcher.repoExists(optimismNode))) - throw Error(`Repository ${optimismNode} does not exist`);*/ - - const hash = "/ipfs/QmVJV1ZbDyTNDReeNu6ykQfUXZ4SKXmLFiqULQ3S1JYcXG"; + if (!(await releaseFetcher.repoExists(optimismNode))) + throw Error(`Repository ${optimismNode} does not exist`); - const pkgData = await getPkgData(releaseFetcher, hash); + const pkgData = await getPkgData(releaseFetcher, optimismNode); const mainnetRpcUrl = getOptimismNodeRpcUrl(); const isRunning = getIsRunning(pkgData, dnpList); resolve({ @@ -97,12 +88,10 @@ export async function getOptimismConfig(): Promise<OptimismConfigGet> { archive: await new Promise<OptimismItem<"archive">>(resolve => { (async () => { try { - /**if (!(await releaseFetcher.repoExists(optimismL2Geth))) - throw Error(`Repository ${optimismL2Geth} does not exist`);*/ - - const hash = "/ipfs/QmWm346aWuktihXB4jQ5QyBfyx1wPdcyXkrRbrq3tKyVnd"; + if (!(await releaseFetcher.repoExists(optimismL2Geth))) + throw Error(`Repository ${optimismL2Geth} does not exist`); - const pkgData = await getPkgData(releaseFetcher, hash); + const pkgData = await getPkgData(releaseFetcher, optimismL2Geth); const isRunning = getIsRunning(pkgData, dnpList); resolve({ status: "ok",