diff --git a/launcher/package.json b/launcher/package.json index 87ffcd1b5..a677e4fc3 100755 --- a/launcher/package.json +++ b/launcher/package.json @@ -18,7 +18,7 @@ "test-coverage": "jest --coverage", "watch:css": "npx tailwindcss -i ./src/main.css -o ./public/output.css --watch", "lint:fix": "eslint --ext .js,.vue --ignore-path ../.gitignore --fix src", - "max": "concurrently \"npm:electron:serve\" \"npm:watch:css\"", + "stereum": "concurrently \"npm:electron:serve\" \"npm:watch:css\"", "format": "prettier . --write" }, "dependencies": { @@ -44,6 +44,7 @@ "pinia": "^2.0.33", "qrcode": "^1.5.1", "ssh2": "^1.1.0", + "uuid": "^9.0.1", "vue": "^3.2.33", "vue-i18n": "9.2.2", "vue-router": "^4.0.15", @@ -88,5 +89,4 @@ "type": "git", "url": "git@github.com:stereum-dev/ethereum-node.git" } -} - +} \ No newline at end of file diff --git a/launcher/public/img/icon/the-staking/alice.gif b/launcher/public/animation/staking/alice.gif similarity index 100% rename from launcher/public/img/icon/the-staking/alice.gif rename to launcher/public/animation/staking/alice.gif diff --git a/launcher/public/img/icon/manage-node-icons/backtonode.png b/launcher/public/img/icon/manage-node-icons/backtonode.png new file mode 100644 index 000000000..dbc689843 Binary files /dev/null and b/launcher/public/img/icon/manage-node-icons/backtonode.png differ diff --git a/launcher/public/img/icon/node-icons/resync.png b/launcher/public/img/icon/node-icons/resync.png index 1ad44fae4..32783d85c 100644 Binary files a/launcher/public/img/icon/node-icons/resync.png and b/launcher/public/img/icon/node-icons/resync.png differ diff --git a/launcher/public/img/icon/the-staking/Fehlender_validatorenclient.svg b/launcher/public/img/icon/the-staking/Fehlender_validatorenclient.svg deleted file mode 100755 index 5e554315f..000000000 --- a/launcher/public/img/icon/the-staking/Fehlender_validatorenclient.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/launcher/public/img/icon/the-staking/RemoveGroup.png b/launcher/public/img/icon/the-staking/RemoveGroup.png new file mode 100644 index 000000000..1cc788cb5 Binary files /dev/null and b/launcher/public/img/icon/the-staking/RemoveGroup.png differ diff --git a/launcher/public/img/icon/the-staking/backtolist.png b/launcher/public/img/icon/the-staking/backtolist.png new file mode 100644 index 000000000..5bd0b4dd1 Binary files /dev/null and b/launcher/public/img/icon/the-staking/backtolist.png differ diff --git a/launcher/public/img/icon/the-staking/cancel.png b/launcher/public/img/icon/the-staking/cancel.png new file mode 100755 index 000000000..6dea4dbc8 Binary files /dev/null and b/launcher/public/img/icon/the-staking/cancel.png differ diff --git a/launcher/public/img/icon/the-staking/check.png b/launcher/public/img/icon/the-staking/check.png new file mode 100755 index 000000000..d02542a21 Binary files /dev/null and b/launcher/public/img/icon/the-staking/check.png differ diff --git a/launcher/public/img/icon/the-staking/display-name.png b/launcher/public/img/icon/the-staking/display-name.png new file mode 100644 index 000000000..08fdb41af Binary files /dev/null and b/launcher/public/img/icon/the-staking/display-name.png differ diff --git a/launcher/public/img/icon/the-staking/filter.png b/launcher/public/img/icon/the-staking/filter.png new file mode 100644 index 000000000..7393e6296 Binary files /dev/null and b/launcher/public/img/icon/the-staking/filter.png differ diff --git a/launcher/public/img/icon/the-staking/group.png b/launcher/public/img/icon/the-staking/group.png new file mode 100644 index 000000000..489466bb7 Binary files /dev/null and b/launcher/public/img/icon/the-staking/group.png differ diff --git a/launcher/public/img/icon/the-staking/hide.png b/launcher/public/img/icon/the-staking/hide.png new file mode 100644 index 000000000..7dc15ce04 Binary files /dev/null and b/launcher/public/img/icon/the-staking/hide.png differ diff --git a/launcher/public/img/icon/the-staking/invisible.png b/launcher/public/img/icon/the-staking/invisible.png new file mode 100755 index 000000000..abf8bc4cc Binary files /dev/null and b/launcher/public/img/icon/the-staking/invisible.png differ diff --git a/launcher/public/img/icon/the-staking/key-sign.png b/launcher/public/img/icon/the-staking/key-sign.png new file mode 100644 index 000000000..d5f11a001 Binary files /dev/null and b/launcher/public/img/icon/the-staking/key-sign.png differ diff --git a/launcher/public/img/icon/the-staking/keyIcon.png b/launcher/public/img/icon/the-staking/keyIcon.png index ccdeac061..73479e504 100755 Binary files a/launcher/public/img/icon/the-staking/keyIcon.png and b/launcher/public/img/icon/the-staking/keyIcon.png differ diff --git a/launcher/public/img/icon/the-staking/list1.png b/launcher/public/img/icon/the-staking/list1.png new file mode 100755 index 000000000..389add0cc Binary files /dev/null and b/launcher/public/img/icon/the-staking/list1.png differ diff --git a/launcher/public/img/icon/the-staking/list2.png b/launcher/public/img/icon/the-staking/list2.png new file mode 100755 index 000000000..0ad677e10 Binary files /dev/null and b/launcher/public/img/icon/the-staking/list2.png differ diff --git a/launcher/public/img/icon/the-staking/open-group.png b/launcher/public/img/icon/the-staking/open-group.png new file mode 100644 index 000000000..af330636b Binary files /dev/null and b/launcher/public/img/icon/the-staking/open-group.png differ diff --git a/launcher/public/img/icon/the-staking/remove-group.png b/launcher/public/img/icon/the-staking/remove-group.png new file mode 100644 index 000000000..1f8e86964 Binary files /dev/null and b/launcher/public/img/icon/the-staking/remove-group.png differ diff --git a/launcher/public/img/icon/the-staking/rename-group.png b/launcher/public/img/icon/the-staking/rename-group.png new file mode 100644 index 000000000..9016eab8d Binary files /dev/null and b/launcher/public/img/icon/the-staking/rename-group.png differ diff --git a/launcher/public/img/icon/the-staking/reset.png b/launcher/public/img/icon/the-staking/reset.png new file mode 100644 index 000000000..15ac39812 Binary files /dev/null and b/launcher/public/img/icon/the-staking/reset.png differ diff --git a/launcher/public/img/icon/the-staking/search.png b/launcher/public/img/icon/the-staking/search.png new file mode 100755 index 000000000..e4f1aa50e Binary files /dev/null and b/launcher/public/img/icon/the-staking/search.png differ diff --git a/launcher/public/img/icon/the-staking/select-key.png b/launcher/public/img/icon/the-staking/select-key.png new file mode 100644 index 000000000..d5f11a001 Binary files /dev/null and b/launcher/public/img/icon/the-staking/select-key.png differ diff --git a/launcher/public/img/icon/the-staking/staking-disabled.png b/launcher/public/img/icon/the-staking/staking-disabled.png new file mode 100644 index 000000000..2f520fc9a Binary files /dev/null and b/launcher/public/img/icon/the-staking/staking-disabled.png differ diff --git a/launcher/public/img/icon/the-staking/ungroup.png b/launcher/public/img/icon/the-staking/ungroup.png new file mode 100644 index 000000000..1b5162400 Binary files /dev/null and b/launcher/public/img/icon/the-staking/ungroup.png differ diff --git a/launcher/public/img/icon/the-staking/validator-import.gif b/launcher/public/img/icon/the-staking/validator-import.gif deleted file mode 100755 index ab988f8a2..000000000 Binary files a/launcher/public/img/icon/the-staking/validator-import.gif and /dev/null differ diff --git a/launcher/public/img/icon/the-staking/visible.png b/launcher/public/img/icon/the-staking/visible.png new file mode 100755 index 000000000..dd528ce5f Binary files /dev/null and b/launcher/public/img/icon/the-staking/visible.png differ diff --git a/launcher/public/output.css b/launcher/public/output.css index baf5292d9..1f718d8d0 100755 --- a/launcher/public/output.css +++ b/launcher/public/output.css @@ -765,6 +765,10 @@ video { top: -0.5rem; } +.-top-\[90px\]{ + top: -90px; +} + .bottom-1{ bottom: 0.25rem; } @@ -801,6 +805,10 @@ video { left: 0.25rem; } +.left-14{ + left: 3.5rem; +} + .left-\[100px\]{ left: 100px; } @@ -893,10 +901,6 @@ video { top: -2px; } -.top-16{ - top: 4rem; -} - .z-0{ z-index: 0; } @@ -977,18 +981,42 @@ video { grid-column-start: 12; } +.col-start-13{ + grid-column-start: 13; +} + +.col-start-15{ + grid-column-start: 15; +} + .col-start-17{ grid-column-start: 17; } +.col-start-18{ + grid-column-start: 18; +} + .col-start-2{ grid-column-start: 2; } +.col-start-20{ + grid-column-start: 20; +} + .col-start-21{ grid-column-start: 21; } +.col-start-23{ + grid-column-start: 23; +} + +.col-start-24{ + grid-column-start: 24; +} + .col-start-3{ grid-column-start: 3; } @@ -1033,10 +1061,18 @@ video { grid-column-end: 13; } +.col-end-15{ + grid-column-end: 15; +} + .col-end-17{ grid-column-end: 17; } +.col-end-18{ + grid-column-end: 18; +} + .col-end-2{ grid-column-end: 2; } @@ -1081,6 +1117,14 @@ video { grid-column-end: 7; } +.col-end-8{ + grid-column-end: 8; +} + +.col-end-9{ + grid-column-end: 9; +} + .row-span-1{ grid-row: span 1 / span 1; } @@ -1089,6 +1133,10 @@ video { grid-row: span 2 / span 2; } +.row-span-3{ + grid-row: span 3 / span 3; +} + .row-span-4{ grid-row: span 4 / span 4; } @@ -1109,6 +1157,10 @@ video { grid-row-start: 1; } +.row-start-10{ + grid-row-start: 10; +} + .row-start-11{ grid-row-start: 11; } @@ -1141,6 +1193,14 @@ video { grid-row-start: 7; } +.row-start-9{ + grid-row-start: 9; +} + +.row-end-10{ + grid-row-end: 10; +} + .row-end-11{ grid-row-end: 11; } @@ -1169,10 +1229,18 @@ video { grid-row-end: 5; } +.row-end-6{ + grid-row-end: 6; +} + .row-end-7{ grid-row-end: 7; } +.row-end-9{ + grid-row-end: 9; +} + .-m-1{ margin: -0.25rem; } @@ -1186,6 +1254,11 @@ video { margin-right: 0.25rem; } +.mx-2{ + margin-left: 0.5rem; + margin-right: 0.5rem; +} + .mx-20{ margin-left: 5rem; margin-right: 5rem; @@ -1210,6 +1283,10 @@ video { margin-bottom: 0.5rem; } +.mb-\[1px\]{ + margin-bottom: 1px; +} + .ml-1{ margin-left: 0.25rem; } @@ -1226,6 +1303,10 @@ video { margin-left: 1rem; } +.ml-5{ + margin-left: 1.25rem; +} + .ml-\[25px\]{ margin-left: 25px; } @@ -1371,6 +1452,14 @@ video { height: 5rem; } +.h-24{ + height: 6rem; +} + +.h-28{ + height: 7rem; +} + .h-3{ height: 0.75rem; } @@ -1503,6 +1592,12 @@ video { height: 70px; } +.h-fit{ + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; +} + .h-full{ height: 100%; } @@ -1511,6 +1606,18 @@ video { height: 100vh; } +.max-h-28{ + max-height: 7rem; +} + +.max-h-36{ + max-height: 9rem; +} + +.max-h-6{ + max-height: 1.5rem; +} + .max-h-60{ max-height: 15rem; } @@ -1523,22 +1630,46 @@ video { max-height: 110px; } +.max-h-\[144px\]{ + max-height: 144px; +} + .max-h-\[150px\]{ max-height: 150px; } +.max-h-\[165px\]{ + max-height: 165px; +} + .max-h-\[170px\]{ max-height: 170px; } +.max-h-\[185px\]{ + max-height: 185px; +} + .max-h-\[200px\]{ max-height: 200px; } +.max-h-\[28px\]{ + max-height: 28px; +} + .max-h-\[320px\]{ max-height: 320px; } +.max-h-\[32px\]{ + max-height: 32px; +} + +.max-h-\[35px\]{ + max-height: 35px; +} + .max-h-\[40px\]{ max-height: 40px; } @@ -1551,6 +1682,10 @@ video { max-height: 430px; } +.max-h-\[489px\]{ + max-height: 489px; +} + .max-h-\[503px\]{ max-height: 503px; } @@ -1567,6 +1702,10 @@ video { max-height: 100%; } +.min-h-\[100px\]{ + min-height: 100px; +} + .min-h-\[18px\]{ min-height: 18px; } @@ -1591,10 +1730,6 @@ video { min-height: 450px; } -.min-h-\[100px\]{ - min-height: 100px; -} - .w-1\/2{ width: 50%; } @@ -1659,6 +1794,10 @@ video { width: 60%; } +.w-36{ + width: 9rem; +} + .w-4{ width: 1rem; } @@ -1699,10 +1838,18 @@ video { width: 2rem; } +.w-80{ + width: 20rem; +} + .w-9{ width: 2.25rem; } +.w-9\/12{ + width: 75%; +} + .w-\[110px\]{ width: 110px; } @@ -1719,12 +1866,12 @@ video { width: 155px; } -.w-\[15px\]{ - width: 15px; +.w-\[156px\]{ + width: 156px; } -.w-\[160px\]{ - width: 160px; +.w-\[15px\]{ + width: 15px; } .w-\[178px\]{ @@ -1856,6 +2003,12 @@ video { transform-origin: top right; } +.-rotate-90{ + --tw-rotate: -90deg; + -webkit-transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .rotate-180{ --tw-rotate: 180deg; -webkit-transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -1908,6 +2061,27 @@ video { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } +@-webkit-keyframes ping{ + 75%, 100%{ + -webkit-transform: scale(2); + transform: scale(2); + opacity: 0; + } +} + +@keyframes ping{ + 75%, 100%{ + -webkit-transform: scale(2); + transform: scale(2); + opacity: 0; + } +} + +.animate-ping{ + -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; + animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; +} + @-webkit-keyframes pulse{ 50%{ opacity: .5; @@ -2000,6 +2174,10 @@ video { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-cols-4{ + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + .grid-cols-5{ grid-template-columns: repeat(5, minmax(0, 1fr)); } @@ -2008,6 +2186,10 @@ video { grid-template-columns: repeat(6, minmax(0, 1fr)); } +.grid-cols-7{ + grid-template-columns: repeat(7, minmax(0, 1fr)); +} + .grid-cols-9{ grid-template-columns: repeat(9, minmax(0, 1fr)); } @@ -2128,6 +2310,18 @@ video { gap: 1.25rem; } +.gap-x-2{ + -webkit-column-gap: 0.5rem; + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} + +.gap-x-8{ + -webkit-column-gap: 2rem; + -moz-column-gap: 2rem; + column-gap: 2rem; +} + .gap-y-1{ row-gap: 0.25rem; } @@ -2144,12 +2338,6 @@ video { row-gap: 1.25rem; } -.gap-x-2{ - -webkit-column-gap: 0.5rem; - -moz-column-gap: 0.5rem; - column-gap: 0.5rem; -} - .space-x-1 > :not([hidden]) ~ :not([hidden]){ --tw-space-x-reverse: 0; margin-right: calc(0.25rem * var(--tw-space-x-reverse)); @@ -2309,10 +2497,18 @@ video { white-space: pre; } +.whitespace-pre-wrap{ + white-space: pre-wrap; +} + .break-words{ overflow-wrap: break-word; } +.break-all{ + word-break: break-all; +} + .rounded{ border-radius: 0.25rem; } @@ -2350,6 +2546,16 @@ video { border-bottom-left-radius: 0.5rem; } +.rounded-b-md{ + border-bottom-right-radius: 0.375rem; + border-bottom-left-radius: 0.375rem; +} + +.rounded-b-sm{ + border-bottom-right-radius: 0.125rem; + border-bottom-left-radius: 0.125rem; +} + .rounded-l-full{ border-top-left-radius: 9999px; border-bottom-left-radius: 9999px; @@ -2365,6 +2571,11 @@ video { border-bottom-right-radius: 9999px; } +.rounded-r-md{ + border-top-right-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; +} + .rounded-t-\[5px\]{ border-top-left-radius: 5px; border-top-right-radius: 5px; @@ -2425,6 +2636,10 @@ video { border-bottom-width: 1px; } +.border-b-2{ + border-bottom-width: 2px; +} + .border-l{ border-left-width: 1px; } @@ -2437,6 +2652,10 @@ video { border-right-width: 2px; } +.border-s-4{ + border-inline-start-width: 4px; +} + .border-t-2{ border-top-width: 2px; } @@ -2449,6 +2668,11 @@ video { border-style: none; } +.border-\[\#171D22\]{ + --tw-border-opacity: 1; + border-color: rgb(23 29 34 / var(--tw-border-opacity)); +} + .border-\[\#1c3634\]{ --tw-border-opacity: 1; border-color: rgb(28 54 52 / var(--tw-border-opacity)); @@ -2469,6 +2693,16 @@ video { border-color: rgb(51 102 102 / var(--tw-border-opacity)); } +.border-\[\#3e4347\]{ + --tw-border-opacity: 1; + border-color: rgb(62 67 71 / var(--tw-border-opacity)); +} + +.border-\[\#6c7e78\]{ + --tw-border-opacity: 1; + border-color: rgb(108 126 120 / var(--tw-border-opacity)); +} + .border-blue-100{ --tw-border-opacity: 1; border-color: rgb(219 234 254 / var(--tw-border-opacity)); @@ -2489,11 +2723,21 @@ video { border-color: rgb(59 130 246 / var(--tw-border-opacity)); } +.border-gray-100{ + --tw-border-opacity: 1; + border-color: rgb(243 244 246 / var(--tw-border-opacity)); +} + .border-gray-200{ --tw-border-opacity: 1; border-color: rgb(229 231 235 / var(--tw-border-opacity)); } +.border-gray-300{ + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + .border-gray-400{ --tw-border-opacity: 1; border-color: rgb(156 163 175 / var(--tw-border-opacity)); @@ -2529,6 +2773,11 @@ video { border-color: rgb(249 115 22 / var(--tw-border-opacity)); } +.border-red-400{ + --tw-border-opacity: 1; + border-color: rgb(248 113 113 / var(--tw-border-opacity)); +} + .border-red-500{ --tw-border-opacity: 1; border-color: rgb(239 68 68 / var(--tw-border-opacity)); @@ -2582,6 +2831,16 @@ video { border-right-color: transparent; } +.bg-\[\#0d0d0e\]{ + --tw-bg-opacity: 1; + background-color: rgb(13 13 14 / var(--tw-bg-opacity)); +} + +.bg-\[\#111213\]{ + --tw-bg-opacity: 1; + background-color: rgb(17 18 19 / var(--tw-bg-opacity)); +} + .bg-\[\#111315\]{ --tw-bg-opacity: 1; background-color: rgb(17 19 21 / var(--tw-bg-opacity)); @@ -2612,6 +2871,11 @@ video { background-color: rgb(21 26 30 / var(--tw-bg-opacity)); } +.bg-\[\#171D22\]{ + --tw-bg-opacity: 1; + background-color: rgb(23 29 34 / var(--tw-bg-opacity)); +} + .bg-\[\#171a1b\]{ --tw-bg-opacity: 1; background-color: rgb(23 26 27 / var(--tw-bg-opacity)); @@ -2622,6 +2886,11 @@ video { background-color: rgb(23 26 28 / var(--tw-bg-opacity)); } +.bg-\[\#17A2B8\]{ + --tw-bg-opacity: 1; + background-color: rgb(23 162 184 / var(--tw-bg-opacity)); +} + .bg-\[\#191b1e\]{ --tw-bg-opacity: 1; background-color: rgb(25 27 30 / var(--tw-bg-opacity)); @@ -2647,6 +2916,11 @@ video { background-color: rgb(27 28 28 / var(--tw-bg-opacity)); } +.bg-\[\#1b1d1f\]{ + --tw-bg-opacity: 1; + background-color: rgb(27 29 31 / var(--tw-bg-opacity)); +} + .bg-\[\#1c1d1d\]{ --tw-bg-opacity: 1; background-color: rgb(28 29 29 / var(--tw-bg-opacity)); @@ -2667,6 +2941,11 @@ video { background-color: rgb(31 33 35 / var(--tw-bg-opacity)); } +.bg-\[\#202123\]{ + --tw-bg-opacity: 1; + background-color: rgb(32 33 35 / var(--tw-bg-opacity)); +} + .bg-\[\#212629\]{ --tw-bg-opacity: 1; background-color: rgb(33 38 41 / var(--tw-bg-opacity)); @@ -2677,6 +2956,11 @@ video { background-color: rgb(33 39 44 / var(--tw-bg-opacity)); } +.bg-\[\#222526\]{ + --tw-bg-opacity: 1; + background-color: rgb(34 37 38 / var(--tw-bg-opacity)); +} + .bg-\[\#224141\]{ --tw-bg-opacity: 1; background-color: rgb(34 65 65 / var(--tw-bg-opacity)); @@ -2687,11 +2971,21 @@ video { background-color: rgb(35 39 44 / var(--tw-bg-opacity)); } +.bg-\[\#242628\]{ + --tw-bg-opacity: 1; + background-color: rgb(36 38 40 / var(--tw-bg-opacity)); +} + .bg-\[\#243d36\]{ --tw-bg-opacity: 1; background-color: rgb(36 61 54 / var(--tw-bg-opacity)); } +.bg-\[\#252525\]{ + --tw-bg-opacity: 1; + background-color: rgb(37 37 37 / var(--tw-bg-opacity)); +} + .bg-\[\#262626\]{ --tw-bg-opacity: 1; background-color: rgb(38 38 38 / var(--tw-bg-opacity)); @@ -2732,6 +3026,11 @@ video { background-color: rgb(48 92 89 / var(--tw-bg-opacity)); } +.bg-\[\#313539\]{ + --tw-bg-opacity: 1; + background-color: rgb(49 53 57 / var(--tw-bg-opacity)); +} + .bg-\[\#32363A\]{ --tw-bg-opacity: 1; background-color: rgb(50 54 58 / var(--tw-bg-opacity)); @@ -2747,6 +3046,11 @@ video { background-color: rgb(51 57 62 / var(--tw-bg-opacity)); } +.bg-\[\#334B3F\]{ + --tw-bg-opacity: 1; + background-color: rgb(51 75 63 / var(--tw-bg-opacity)); +} + .bg-\[\#334d4d\]{ --tw-bg-opacity: 1; background-color: rgb(51 77 77 / var(--tw-bg-opacity)); @@ -2757,6 +3061,11 @@ video { background-color: rgb(51 102 102 / var(--tw-bg-opacity)); } +.bg-\[\#343434\]{ + --tw-bg-opacity: 1; + background-color: rgb(52 52 52 / var(--tw-bg-opacity)); +} + .bg-\[\#38504e\]{ --tw-bg-opacity: 1; background-color: rgb(56 80 78 / var(--tw-bg-opacity)); @@ -2777,6 +3086,16 @@ video { background-color: rgb(61 68 73 / var(--tw-bg-opacity)); } +.bg-\[\#3e4347\]{ + --tw-bg-opacity: 1; + background-color: rgb(62 67 71 / var(--tw-bg-opacity)); +} + +.bg-\[\#4D56CB\]{ + --tw-bg-opacity: 1; + background-color: rgb(77 86 203 / var(--tw-bg-opacity)); +} + .bg-\[\#4d7575\]{ --tw-bg-opacity: 1; background-color: rgb(77 117 117 / var(--tw-bg-opacity)); @@ -2792,11 +3111,31 @@ video { background-color: rgb(83 114 99 / var(--tw-bg-opacity)); } +.bg-\[\#578f84\]{ + --tw-bg-opacity: 1; + background-color: rgb(87 143 132 / var(--tw-bg-opacity)); +} + .bg-\[\#609879\]{ --tw-bg-opacity: 1; background-color: rgb(96 152 121 / var(--tw-bg-opacity)); } +.bg-\[\#a7aeb5\]{ + --tw-bg-opacity: 1; + background-color: rgb(167 174 181 / var(--tw-bg-opacity)); +} + +.bg-\[\#a846b8\]{ + --tw-bg-opacity: 1; + background-color: rgb(168 70 184 / var(--tw-bg-opacity)); +} + +.bg-amber-400{ + --tw-bg-opacity: 1; + background-color: rgb(251 191 36 / var(--tw-bg-opacity)); +} + .bg-black{ --tw-bg-opacity: 1; background-color: rgb(0 0 0 / var(--tw-bg-opacity)); @@ -2807,6 +3146,11 @@ video { background-color: rgb(96 165 250 / var(--tw-bg-opacity)); } +.bg-blue-500{ + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + .bg-cyan-300{ --tw-bg-opacity: 1; background-color: rgb(103 232 249 / var(--tw-bg-opacity)); @@ -2857,6 +3201,11 @@ video { background-color: rgb(17 24 39 / var(--tw-bg-opacity)); } +.bg-green-400{ + --tw-bg-opacity: 1; + background-color: rgb(74 222 128 / var(--tw-bg-opacity)); +} + .bg-green-500{ --tw-bg-opacity: 1; background-color: rgb(34 197 94 / var(--tw-bg-opacity)); @@ -2877,6 +3226,11 @@ video { background-color: rgb(249 115 22 / var(--tw-bg-opacity)); } +.bg-red-100{ + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} + .bg-red-500{ --tw-bg-opacity: 1; background-color: rgb(239 68 68 / var(--tw-bg-opacity)); @@ -2897,6 +3251,11 @@ video { background-color: rgb(153 27 27 / var(--tw-bg-opacity)); } +.bg-slate-200{ + --tw-bg-opacity: 1; + background-color: rgb(226 232 240 / var(--tw-bg-opacity)); +} + .bg-slate-300{ --tw-bg-opacity: 1; background-color: rgb(203 213 225 / var(--tw-bg-opacity)); @@ -3021,6 +3380,14 @@ video { padding: 2rem; } +.p-\[1px\]{ + padding: 1px; +} + +.p-\[2px\]{ + padding: 2px; +} + .px-1{ padding-left: 0.25rem; padding-right: 0.25rem; @@ -3056,6 +3423,16 @@ video { padding-right: 2rem; } +.px-\[1px\]{ + padding-left: 1px; + padding-right: 1px; +} + +.px-\[2px\]{ + padding-left: 2px; + padding-right: 2px; +} + .py-1{ padding-top: 0.25rem; padding-bottom: 0.25rem; @@ -3076,6 +3453,11 @@ video { padding-bottom: 1rem; } +.py-\[2px\]{ + padding-top: 2px; + padding-bottom: 2px; +} + .pb-1{ padding-bottom: 0.25rem; } @@ -3096,6 +3478,14 @@ video { padding-left: 0.5rem; } +.pl-4{ + padding-left: 1rem; +} + +.pr-0{ + padding-right: 0px; +} + .pr-2{ padding-right: 0.5rem; } @@ -3144,15 +3534,27 @@ video { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } +.font-sans{ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + .text-2xl{ font-size: 1.5rem; line-height: 2rem; } +.text-2xs{ + font-size: 10px; +} + .text-\[10px\]{ font-size: 10px; } +.text-\[11px\]{ + font-size: 11px; +} + .text-\[12px\]{ font-size: 12px; } @@ -3243,6 +3645,11 @@ video { color: rgb(75 135 141 / var(--tw-text-opacity)); } +.text-amber-300{ + --tw-text-opacity: 1; + color: rgb(252 211 77 / var(--tw-text-opacity)); +} + .text-amber-400{ --tw-text-opacity: 1; color: rgb(251 191 36 / var(--tw-text-opacity)); @@ -3263,6 +3670,11 @@ video { color: rgb(96 165 250 / var(--tw-text-opacity)); } +.text-cyan-400{ + --tw-text-opacity: 1; + color: rgb(34 211 238 / var(--tw-text-opacity)); +} + .text-cyan-500{ --tw-text-opacity: 1; color: rgb(6 182 212 / var(--tw-text-opacity)); @@ -3328,6 +3740,11 @@ video { color: rgb(34 197 94 / var(--tw-text-opacity)); } +.text-green-600{ + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + .text-green-700{ --tw-text-opacity: 1; color: rgb(21 128 61 / var(--tw-text-opacity)); @@ -3353,6 +3770,16 @@ video { color: rgb(185 28 28 / var(--tw-text-opacity)); } +.text-red-800{ + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + +.text-teal-400{ + --tw-text-opacity: 1; + color: rgb(45 212 191 / var(--tw-text-opacity)); +} + .text-teal-500{ --tw-text-opacity: 1; color: rgb(20 184 166 / var(--tw-text-opacity)); @@ -3383,6 +3810,26 @@ video { color: rgb(234 179 8 / var(--tw-text-opacity)); } +.placeholder-gray-400\/70::-webkit-input-placeholder{ + color: rgb(156 163 175 / 0.7); +} + +.placeholder-gray-400\/70::-moz-placeholder{ + color: rgb(156 163 175 / 0.7); +} + +.placeholder-gray-400\/70:-ms-input-placeholder{ + color: rgb(156 163 175 / 0.7); +} + +.placeholder-gray-400\/70::-ms-input-placeholder{ + color: rgb(156 163 175 / 0.7); +} + +.placeholder-gray-400\/70::placeholder{ + color: rgb(156 163 175 / 0.7); +} + .placeholder-gray-500::-webkit-input-placeholder{ --tw-placeholder-opacity: 1; color: rgb(107 114 128 / var(--tw-placeholder-opacity)); @@ -3408,6 +3855,31 @@ video { color: rgb(107 114 128 / var(--tw-placeholder-opacity)); } +.placeholder-red-500::-webkit-input-placeholder{ + --tw-placeholder-opacity: 1; + color: rgb(239 68 68 / var(--tw-placeholder-opacity)); +} + +.placeholder-red-500::-moz-placeholder{ + --tw-placeholder-opacity: 1; + color: rgb(239 68 68 / var(--tw-placeholder-opacity)); +} + +.placeholder-red-500:-ms-input-placeholder{ + --tw-placeholder-opacity: 1; + color: rgb(239 68 68 / var(--tw-placeholder-opacity)); +} + +.placeholder-red-500::-ms-input-placeholder{ + --tw-placeholder-opacity: 1; + color: rgb(239 68 68 / var(--tw-placeholder-opacity)); +} + +.placeholder-red-500::placeholder{ + --tw-placeholder-opacity: 1; + color: rgb(239 68 68 / var(--tw-placeholder-opacity)); +} + .opacity-0{ opacity: 0; } @@ -3436,6 +3908,10 @@ video { opacity: 0.7; } +.opacity-75{ + opacity: 0.75; +} + .opacity-80{ opacity: 0.8; } @@ -3492,6 +3968,11 @@ video { --tw-shadow: var(--tw-shadow-colored); } +.shadow-\[\#111010\]{ + --tw-shadow-color: #111010; + --tw-shadow: var(--tw-shadow-colored); +} + .shadow-\[\#141516\]{ --tw-shadow-color: #141516; --tw-shadow: var(--tw-shadow-colored); @@ -3502,6 +3983,11 @@ video { --tw-shadow: var(--tw-shadow-colored); } +.shadow-\[\#191a1b\]{ + --tw-shadow-color: #191a1b; + --tw-shadow: var(--tw-shadow-colored); +} + .shadow-\[\#1b1b1b\]{ --tw-shadow-color: #1b1b1b; --tw-shadow: var(--tw-shadow-colored); @@ -3517,6 +4003,11 @@ video { --tw-shadow: var(--tw-shadow-colored); } +.shadow-\[\#242c29\]{ + --tw-shadow-color: #242c29; + --tw-shadow: var(--tw-shadow-colored); +} + .shadow-\[\#252525\]{ --tw-shadow-color: #252525; --tw-shadow: var(--tw-shadow-colored); @@ -3765,6 +4256,31 @@ html body { background-color: rgb(31, 31, 31); } +.placeholder\:text-gray-400::-webkit-input-placeholder{ + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-400::-moz-placeholder{ + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-400:-ms-input-placeholder{ + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-400::-ms-input-placeholder{ + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-400::placeholder{ + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + .placeholder\:text-gray-500::-webkit-input-placeholder{ --tw-text-opacity: 1; color: rgb(107 114 128 / var(--tw-text-opacity)); @@ -3946,6 +4462,11 @@ html body { border-width: 1px; } +.hover\:border-\[\#3f4851\]:hover{ + --tw-border-opacity: 1; + border-color: rgb(63 72 81 / var(--tw-border-opacity)); +} + .hover\:border-blue-400:hover{ --tw-border-opacity: 1; border-color: rgb(96 165 250 / var(--tw-border-opacity)); @@ -3981,6 +4502,11 @@ html body { border-color: rgb(13 148 136 / var(--tw-border-opacity)); } +.hover\:bg-\[\#212325\]:hover{ + --tw-bg-opacity: 1; + background-color: rgb(33 35 37 / var(--tw-bg-opacity)); +} + .hover\:bg-\[\#224141\]:hover{ --tw-bg-opacity: 1; background-color: rgb(34 65 65 / var(--tw-bg-opacity)); @@ -3991,6 +4517,11 @@ html body { background-color: rgb(35 39 42 / var(--tw-bg-opacity)); } +.hover\:bg-\[\#234545\]:hover{ + --tw-bg-opacity: 1; + background-color: rgb(35 69 69 / var(--tw-bg-opacity)); +} + .hover\:bg-\[\#243535\]:hover{ --tw-bg-opacity: 1; background-color: rgb(36 53 53 / var(--tw-bg-opacity)); @@ -4096,6 +4627,11 @@ html body { background-color: rgb(185 28 28 / var(--tw-bg-opacity)); } +.hover\:bg-slate-300:hover{ + --tw-bg-opacity: 1; + background-color: rgb(203 213 225 / var(--tw-bg-opacity)); +} + .hover\:bg-slate-400:hover{ --tw-bg-opacity: 1; background-color: rgb(148 163 184 / var(--tw-bg-opacity)); @@ -4111,6 +4647,11 @@ html body { background-color: rgb(17 94 89 / var(--tw-bg-opacity)); } +.hover\:text-gray-300:hover{ + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + .hover\:shadow-lg:hover{ --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -4137,6 +4678,11 @@ html body { --tw-shadow: var(--tw-shadow-colored); } +.hover\:shadow-\[\#101214\]:hover{ + --tw-shadow-color: #101214; + --tw-shadow: var(--tw-shadow-colored); +} + .hover\:shadow-\[\#1a1b1b\]:hover{ --tw-shadow-color: #1a1b1b; --tw-shadow: var(--tw-shadow-colored); @@ -4248,6 +4794,14 @@ html body { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } +:is([dir="rtl"] .rtl\:left-0){ + left: 0px; +} + +:is([dir="rtl"] .rtl\:right-auto){ + right: auto; +} + @media (prefers-color-scheme: dark){ .dark\:text-gray-300{ --tw-text-opacity: 1; diff --git a/launcher/src/backend/Monitoring.js b/launcher/src/backend/Monitoring.js index 3e8a5b7f4..b9b37ca6a 100755 --- a/launcher/src/backend/Monitoring.js +++ b/launcher/src/backend/Monitoring.js @@ -2834,144 +2834,87 @@ rm -rf diskoutput return serviceInfos; } - async getValidatorStats(validatorPublicKey) { + async getCurrentEpochandSlot() { try { - const verbose = true; - const proposer = false; - - const beaconStatus = await this.getBeaconStatus(); - const beaconAPIPort = beaconStatus.data[0].beacon.destinationPort; - - const baseURL = `http://localhost:${beaconAPIPort}`; - - const validatorRes = await this.queryBeaconApi( - baseURL, - `/eth/v1/beacon/states/head/validators/${validatorPublicKey}`, - undefined, - "GET" - ); - log.debug(validatorRes); - - const validators_arr = [validatorRes.data.api_reponse.data.index]; - - const beaconAPICmdGenesisTime = `curl -s -X GET '${baseURL}/eth/v1/beacon/genesis' -H 'accept: application/json'`; - const genesisResShell = await this.nodeConnection.sshService.exec(beaconAPICmdGenesisTime); - - const beaconAPICmdSpec = `curl -s -X GET '${baseURL}/eth/v1/config/spec' -H 'accept: application/json'`; - const specRes = await this.nodeConnection.sshService.exec(beaconAPICmdSpec); + // Get local beacon port from first available consensus client + const beaconResult = await this.findBeaconPort(); + if (beaconResult.code) { + throw new Error("error: could not get balancestatus due to missing beacon port (" + beaconResult.info + ")"); + } + const baseURL = `http://127.0.0.1:${beaconResult.data.port}`; - const { SLOTS_PER_EPOCH: slotsPerEpoch, SECONDS_PER_SLOT: secondsPerSlot } = JSON.parse(specRes.stdout).data; + let genesisRes = await this.queryBeaconApi(baseURL, "/eth/v1/beacon/genesis", [], "GET"); + let specRes = await this.queryBeaconApi(baseURL, "/eth/v1/config/spec", [], "GET"); + if (genesisRes.code || specRes.code) { + throw new Error(`Couldn't get genesis or spec:\n${genesisRes.info}\n${specRes.info}`); + } - let output = {}; + const { SLOTS_PER_EPOCH: slotsPerEpoch, SECONDS_PER_SLOT: secondsPerSlot } = specRes.data.api_reponse.data; + const { genesis_time } = genesisRes.data.api_reponse.data; - const { genesis_time } = JSON.parse(genesisResShell.stdout).data; const current_time = Math.floor(Date.now() / 1000); const slot_time = secondsPerSlot; - const slot_timeout = slot_time - ((current_time - genesis_time) % slot_time); + const current_slot = Math.floor((current_time - genesis_time) / slot_time); const current_epoch = Math.floor(current_slot / slotsPerEpoch); - output = { currentEpoch: current_epoch, currentSlot: current_slot }; - - const res = await this.queryBeaconApi( - baseURL, - `/eth/v1/validator/duties/attester/${Math.trunc(current_epoch)}`, - validators_arr, - "POST", - { - "Content-Type": "application/json", - } - ); - - const res_p = await this.queryBeaconApi( - baseURL, - `/eth/v1/validator/duties/proposer/${Math.trunc(current_epoch)}`, - null, - "GET", - { - "Content-Type": "application/json", - } - ); - - let current_prop = 0; - let next_att_slot = 0; - let next_prop_slot = 0; - - const attestationDuties = JSON.stringify(res.data.api_reponse); - const proposerDuties = JSON.stringify(res_p.data.api_reponse); - - // Handle attestation duties - let vidx = attestationDuties.match(/.*"validator_index":"?(\d+)"?.*/)[1]; - let slot = attestationDuties.match(/.*"slot":"?(\d+)"?.*/)[1]; - if (vidx !== undefined && slot !== undefined) { - if (vidx.match(/^[-]?\d+$/) !== null) { - if (verbose === true) { - let duty_eta = (slot - current_slot - 1) * slot_time + slot_timeout; - let eta_str = ""; - if (duty_eta > 0) { - eta_str = " ETA: " + duty_eta + " sec"; - } else if (duty_eta > 0 - slot_time) { - eta_str = " ETA: now!"; - } - let slot_idx = slot % slotsPerEpoch; - output = { ...output, validator: vidx, attestationSlot: slot, idx: slot_idx, ETA: eta_str }; - } - if (slot > current_slot) { - if (slot < next_att_slot || next_att_slot === 0) { - next_att_slot = slot; - } - } - } - } - - // Handle proposer duties - vidx = proposerDuties.match(/.*"validator_index":"?(\d+)"?.*/)[1]; - slot = proposerDuties.match(/.*"slot":"?(\d+)"?.*/)[1]; - if (vidx !== undefined && slot !== undefined) { - if (vidx.match(/^[-]?\d+$/) !== null && validators_arr.includes(vidx) === true) { - if (verbose === true) { - let duty_eta = (slot - current_slot - 1) * slot_time + slot_timeout; - let eta_str = ""; - if (duty_eta >= 0) { - eta_str = " ETA: " + duty_eta + " sec"; - } else if (duty_eta > 0 - slot_time) { - eta_str = " ETA: now!"; - } - let slot_idx = slot % slotsPerEpoch; - output = { ...output, validator: vidx, attestationSlot: slot, idx: slot_idx, ETA: eta_str }; - } - if (slot > current_slot) { - if (slot < next_prop_slot || next_prop_slot === 0) { - next_prop_slot = slot; - } - } else if (slot === current_slot) { - current_prop = vidx; - } - } - } + return { current_epoch, current_slot, secondsPerSlot, slotsPerEpoch }; + } catch (error) { + log.error("Getting Epoch and Slot Failed:\n" + error); + return { + code: error.code ? error.code : 1, + info: error.message ? error.message : error.info, + data: error.data ? error.data : JSON.stringify(error), + }; + } + } - if (proposer === true) { - output = { ...output, nextAttSlot: next_att_slot, nextPropSlot: next_prop_slot }; + // Fetches the validator duties (proposer and sync) for the given validator indices + // Returns an array of validator duties (proposer and sync) for the given validator indices + // validatorIndices: array of validator indices + async getValidatorDuties(validatorIndices) { + try { + if (!Array.isArray(validatorIndices)) { + throw new Error("Invalid Argument: validatorIndices must be an Array"); } - let next_duty_slot; - if (next_prop_slot > 0 && next_prop_slot < next_att_slot) { - next_duty_slot = next_prop_slot; - } else { - next_duty_slot = next_att_slot; + // Get local beacon port from first available consensus client + const beaconResult = await this.findBeaconPort(); + if (beaconResult.code) { + throw new Error("error: could not get balancestatus due to missing beacon port (" + beaconResult.info + ")"); } + const baseURL = `http://127.0.0.1:${beaconResult.data.port}`; - if (next_duty_slot > 0) { - const remaining_slots = next_duty_slot - current_slot - 1; + const { current_epoch, current_slot } = await this.getCurrentEpochandSlot(); - const remaining_time = remaining_slots * slot_time + slot_timeout; - - output = { ...output, remainingSlots: remaining_slots, remainingTime: remaining_time }; - } + let proposerDutiesRes = await this.queryBeaconApi( + baseURL, + "/eth/v1/validator/duties/proposer/" + current_epoch, + [], + "GET" + ); + let syncDutiesRes = await this.queryBeaconApi( + baseURL, + "/eth/v1/validator/duties/sync/" + current_epoch, + validatorIndices, + "POST" + ); - return { ...output, currentProp: current_prop, slotsPerEpoch }; + return { + proposerDuties: proposerDutiesRes.data.api_reponse.data.filter((d) => + validatorIndices.some((i) => i === d.validator_index) + ), // filter out duties for validators that are not in the validatorIndices (imported vals) array + syncDuties: syncDutiesRes.data.api_reponse.data, + currentEpoch: current_epoch, + currentSlot: current_slot, + }; } catch (error) { - return { error: true, message: "An error occurred in getValidatorStats" }; + log.error("Getting Validator Duties Failed:\n" + error); + return { + code: error.code ? error.code : 1, + info: error.message ? error.message : JSON.stringify(error), + data: error, + }; } } @@ -3010,7 +2953,7 @@ rm -rf diskoutput validatorBalances = queryResult.map((key, id) => { return { id: id, - index: key.index, + validatorindex: key.index, balance: key.balance, status: key.validator.slashed === "true" ? "slashed" : key.status.replace(/_.*/, ""), pubkey: key.validator.pubkey, @@ -3156,7 +3099,7 @@ rm -rf diskoutput } } - async exitValidatorAccount(pubkey, password, serviceID) { + async exitValidatorAccount(pubkey, serviceID) { const beaconStatus = await this.getBeaconStatus(); try { if (beaconStatus.code === 0) { @@ -3165,25 +3108,26 @@ rm -rf diskoutput if (!Array.isArray(pubkey)) { pubkey = [pubkey]; } - const results = []; + let results = []; for (let i = 0; i < pubkey.length; i++) { const ref = StringUtils.createRandomString(); // Create a random string to identify the task this.nodeConnection.taskManager.otherTasksHandler(ref, `Exit Account ${pubkey[i].substring(0, 6)}..`); try { - const result = await this.validatorAccountManager.getExitValidatorMessage(pubkey[i], password, serviceID); - if (SSHService.checkExecError(result) && result.stderr) throw SSHService.extractExecError(result); - log.info(result); - - const exitMsg = JSON.stringify(JSON.parse(result.stdout).data); - const exitCommand = `docker run --rm --network=stereum curlimages/curl curl 'http://stereum-${serviceId}:${beaconAPIPort}/eth/v1/beacon/pool/voluntary_exits' -H 'accept: */*' -H 'Content-Type: application/json' -d '${exitMsg}'`; + const result = await this.validatorAccountManager.getExitValidatorMessage(pubkey[i], serviceID); + if (result.data === undefined) { + throw result; + } + const exitMsg = result.data; + const exitCommand = `docker run --rm --network=stereum curlimages/curl curl 'http://stereum-${serviceId}:${beaconAPIPort}/eth/v1/beacon/pool/voluntary_exits' -H 'accept: */*' -H 'Content-Type: application/json' -d '${JSON.stringify( + exitMsg + )}' -i -s`; const runExitCommand = await this.nodeConnection.sshService.exec(exitCommand); - if (SSHService.checkExecError(runExitCommand) && runExitCommand.stderr) - throw SSHService.extractExecError(runExitCommand); + log.info(runExitCommand); - // if (!runExitCommand.stdout.includes("validator_index")) { // find out "successful msg" and put instead of !!!! - // throw "Undexpected Error: " + runExitCommand.stdout; - // } + //Error handling + if (SSHService.checkExecError(runExitCommand) && runExitCommand.stderr) + throw SSHService.extractExecError(runExitCommand); // Push successful task this.nodeConnection.taskManager.otherTasksHandler(ref, `Exiting Account`, true, runExitCommand.stdout); @@ -3191,7 +3135,17 @@ rm -rf diskoutput // add pubkey into the runExitCommands' result; runExitCommand["pubkey"] = `${pubkey[i]}`; - results.push(runExitCommand); + + // Extract the JSON payload from the stdout + const jsonStartIndex = runExitCommand.stdout.indexOf("{"); + const jsonEndIndex = runExitCommand.stdout.lastIndexOf("}"); + const stdoutJson = runExitCommand.stdout.substring(jsonStartIndex, jsonEndIndex + 1); + + results.push({ + pubkey: runExitCommand.pubkey, + code: JSON.parse(stdoutJson).code, + msg: JSON.parse(stdoutJson).message, + }); } catch (error) { this.nodeConnection.taskManager.otherTasksHandler( ref, diff --git a/launcher/src/backend/ValidatorAccountManager.js b/launcher/src/backend/ValidatorAccountManager.js index e8d478957..5290ee84e 100755 --- a/launcher/src/backend/ValidatorAccountManager.js +++ b/launcher/src/backend/ValidatorAccountManager.js @@ -19,8 +19,6 @@ export class ValidatorAccountManager { this.nodeConnection = nodeConnection; this.serviceManager = serviceManager; this.batches = []; - this.callCount = 0; - this.storedKeys = []; } createBatch(files, password, slashingDB, chunkSize = 100) { @@ -49,7 +47,7 @@ export class ValidatorAccountManager { pubkeys = files; } else { this.batches = []; - this.createBatch(files, password, slashingDB, client.service === "Web3SignerService" ? 20 : 100); + this.createBatch(files, password, slashingDB, isRemote ? 20 : 100); pubkeys = this.batches.map((b) => b.keystores.map((c) => JSON.parse(c).pubkey)).flat(); } @@ -61,11 +59,9 @@ export class ValidatorAccountManager { let latestEpochsResponse = await axios.get( networks[client.network].dataEndpoint + "/validator/" + pubkey + "/attestations" ); - if ( - latestEpochsResponse.status === 200 && - latestEpochsResponse.data.data.length > 0 && - latestEpochsResponse.data.status != /ERROR:*/ - ) { + + if (latestEpochsResponse.status === 200 && latestEpochsResponse.data.data.length > 0) { + for (let i = 0; i < 2; i++) { if (latestEpochsResponse.data.data[i].status === 1 && isActiveRunning.indexOf(pubkey) === -1) { isActiveRunning.push(pubkey); @@ -237,7 +233,7 @@ export class ValidatorAccountManager { } } - async listValidators(serviceID, numRunningValidatorService) { + async listValidators(serviceID) { const ref = StringUtils.createRandomString(); this.nodeConnection.taskManager.otherTasksHandler(ref, `Listing Keys`); try { @@ -258,7 +254,7 @@ export class ValidatorAccountManager { this.nodeConnection.taskManager.otherTasksHandler(ref, `Get Keys`, true, result.stdout); if (!data.data) data.data = []; - await this.storeKeys(data.data, numRunningValidatorService); + this.writeKeys(data.data.map((key) => key.validating_pubkey)); this.nodeConnection.taskManager.otherTasksHandler(ref, `Write Keys to keys.yaml`, true); this.nodeConnection.taskManager.otherTasksHandler(ref); @@ -430,7 +426,6 @@ export class ValidatorAccountManager { //Push successful task this.nodeConnection.taskManager.otherTasksHandler(ref, `Set Fee Recipient`, true, result.stdout); this.nodeConnection.taskManager.otherTasksHandler(ref); - return data; } catch (err) { this.nodeConnection.taskManager.otherTasksHandler( @@ -464,7 +459,6 @@ export class ValidatorAccountManager { //Push successful task this.nodeConnection.taskManager.otherTasksHandler(ref, `Delete Fee Recipient`, true, result.stdout); this.nodeConnection.taskManager.otherTasksHandler(ref); - return data; } catch (err) { this.nodeConnection.taskManager.otherTasksHandler( @@ -535,58 +529,35 @@ export class ValidatorAccountManager { } } - async storeKeys(data, numRunningValidatorService) { - this.callCount++; - if (this.callCount % numRunningValidatorService !== 0 && !isNaN(this.callCount % numRunningValidatorService)) { - this.storedKeys.push(...data.map((k) => k.validating_pubkey)); - } else if (this.callCount % numRunningValidatorService === 0) { - this.storedKeys.push(...data.map((k) => k.validating_pubkey)); - await this.writeKeys(this.storedKeys); - this.storedKeys = []; - this.callCount = 0; - } else if (data.length === 0) { - await this.writeKeys(data); - this.callCount = 0; + async writeKeys(keys) { + //get current keys in yaml file + let currentKeys = await this.readKeys(); + if (!currentKeys) { + currentKeys = {}; } - } - async writeKeys(keys) { - let obj = keys; + //if the argument is an array of keys, add them to the current keys if they don't exist if (Array.isArray(keys)) { - const existing = await this.readKeys(); - if (existing) { - obj = existing; - if (Object.keys(existing).length !== 0) { - Object.keys(existing).forEach((key) => { - obj = !keys.includes(key) ? {} : obj; - }); - } - keys.forEach((key) => { - if (existing[key]) { - if (typeof existing[key] === "string") { - obj[key] = { - keyName: existing[key], - groupName: "", - groupID: null, - }; - } else { - obj[key] = existing[key]; - } - } else { - obj[key] = { - keyName: "", - groupName: "", - groupID: null, - }; - } - }); - } else { - obj = {}; + keys.forEach((key) => { + if (!currentKeys[key]) + currentKeys[key] = { keyName: "", groupName: "", groupID: null, validatorClientID: null }; + }); + await this.nodeConnection.sshService.exec( + "echo -e " + StringUtils.escapeStringForShell(YAML.stringify(currentKeys)) + " > /etc/stereum/keys.yaml" + ); + + //if the argument is an object of keys, overwrite the current keys + } else if (keys) { + if (!keys.overwrite) { + keys = { ...currentKeys, ...keys }; } + delete keys.overwrite; + await this.nodeConnection.sshService.exec( + "echo -e " + StringUtils.escapeStringForShell(YAML.stringify(keys)) + " > /etc/stereum/keys.yaml" + ); + } else { + log.error("INVALID ARGUMENT: keys must be an array or an object"); } - await this.nodeConnection.sshService.exec( - "echo -e " + StringUtils.escapeStringForShell(YAML.stringify(obj)) + " > /etc/stereum/keys.yaml" - ); } async readKeys() { @@ -659,25 +630,28 @@ export class ValidatorAccountManager { return result.stdout.trim(); } - async getExitValidatorMessage(pubkey, password, serviceID) { + async getExitValidatorMessage(pubkey, serviceID) { const ref = StringUtils.createRandomString(); //Create a random string to identify the task this.nodeConnection.taskManager.otherTasksHandler(ref, `Exit msg for ${pubkey.substring(0, 6)}..`); try { let service = await this.nodeConnection.readServiceConfiguration(serviceID); - let data = []; - const result = await this.keymanagerAPI(service, "POST", `/eth/v1/validator/${pubkey}/voluntary_exit`, data); + const result = await this.keymanagerAPI(service, "POST", `/eth/v1/validator/${pubkey}/voluntary_exit`, []); if (SSHService.checkExecError(result) && result.stderr) throw SSHService.extractExecError(result); - log.info(result); - if (!result.stdout.includes("validator_index")) { - throw "Undexpected Error: " + result.stdout; + log.info(result); + const data = JSON.parse(result.stdout); + if (data.data === undefined) { + if (data.code === undefined || data.message === undefined) { + throw "Undexpected Error: " + result; + } + throw data.code + " " + data.message; } //Push successful task - this.nodeConnection.taskManager.otherTasksHandler(ref, `Get signed voluntary exit message`, true, result.stdout); + this.nodeConnection.taskManager.otherTasksHandler(ref, `Get signed voluntary exit message`, true, data); this.nodeConnection.taskManager.otherTasksHandler(ref); - return result; + return data; } catch (error) { this.nodeConnection.taskManager.otherTasksHandler( ref, diff --git a/launcher/src/background.js b/launcher/src/background.js index a990e610a..4a9e3f00a 100755 --- a/launcher/src/background.js +++ b/launcher/src/background.js @@ -240,7 +240,7 @@ ipcMain.handle("deleteValidators", async (event, args) => { }); ipcMain.handle("listValidators", async (event, args) => { - return await validatorAccountManager.listValidators(args.serviceID, args.numRunningValidatorService); + return await validatorAccountManager.listValidators(args); }); ipcMain.handle("listServices", async () => { @@ -423,7 +423,7 @@ ipcMain.handle("checkActiveValidators", async (event, args) => { }); ipcMain.handle("exitValidatorAccount", async (event, args) => { - return await monitoring.exitValidatorAccount(args.pubkey, args.password, args.serviceID); + return await monitoring.exitValidatorAccount(args.pubkey, args.serviceID); }); ipcMain.handle("exportConfig", async () => { @@ -494,6 +494,14 @@ ipcMain.handle("dumpDockerLogs", async () => { return await nodeConnection.dumpDockerLogs(); }); +ipcMain.handle("getCurrentEpochandSlot", async () => { + return await monitoring.getCurrentEpochandSlot(); +}); + +ipcMain.handle("getValidatorDuties", async (event, args) => { + return await monitoring.getValidatorDuties(args); +}); + // Scheme must be registered before the app is ready protocol.registerSchemesAsPrivileged([{ scheme: "app", privileges: { secure: true, standard: true } }]); diff --git a/launcher/src/components/UI/custom-installation/components/customHeader.vue b/launcher/src/components/UI/custom-installation/components/customHeader.vue index 4af68778c..993cad604 100644 --- a/launcher/src/components/UI/custom-installation/components/customHeader.vue +++ b/launcher/src/components/UI/custom-installation/components/customHeader.vue @@ -3,9 +3,14 @@
- {{ $t("customInstallation.customInstallationTitle") }}
+ diff --git a/launcher/src/components/UI/edit-page/EditScreen.vue b/launcher/src/components/UI/edit-page/EditScreen.vue index d3bd096a1..dd155698b 100644 --- a/launcher/src/components/UI/edit-page/EditScreen.vue +++ b/launcher/src/components/UI/edit-page/EditScreen.vue @@ -2,7 +2,7 @@ -
+
@@ -120,6 +120,7 @@ import { useNodeHeader } from "@/store/nodeHeader"; import { useStakingStore } from "@/store/theStaking"; import { useDeepClone } from "@/composables/utils"; import { useFooter } from "@/store/theFooter"; +import { useListKeys } from "@/composables/validators"; const footerStore = useFooter(); const serviceStore = useServices(); @@ -181,6 +182,10 @@ onUnmounted(() => { // Methods +const listKeys = async (forceRefresh) => { + await useListKeys(forceRefresh); +}; + // Random ID generator function generateRandomId() { const timestamp = new Date().getTime().toString(16); // Convert timestamp to hexadecimal @@ -572,6 +577,7 @@ const confirmHandler = async () => { manageStore.disableConfirmButton = false; manageStore.isLineHidden = false; }, 4000); + await listKeys(true); }; const nukeConfirmation = () => { diff --git a/launcher/src/components/UI/edit-page/components/edit/ConfigDetails.vue b/launcher/src/components/UI/edit-page/components/edit/ConfigDetails.vue index 6964973f5..9af4b9048 100644 --- a/launcher/src/components/UI/edit-page/components/edit/ConfigDetails.vue +++ b/launcher/src/components/UI/edit-page/components/edit/ConfigDetails.vue @@ -1,7 +1,5 @@ diff --git a/launcher/src/components/UI/node-page/NodeScreen.vue b/launcher/src/components/UI/node-page/NodeScreen.vue index 8967e3ae3..1452b7ff7 100755 --- a/launcher/src/components/UI/node-page/NodeScreen.vue +++ b/launcher/src/components/UI/node-page/NodeScreen.vue @@ -7,17 +7,27 @@
- +
-
+
diff --git a/launcher/src/components/UI/node-page/sections/SidebarSection.vue b/launcher/src/components/UI/node-page/sections/SidebarSection.vue index 3faaf2966..34388403e 100755 --- a/launcher/src/components/UI/node-page/sections/SidebarSection.vue +++ b/launcher/src/components/UI/node-page/sections/SidebarSection.vue @@ -1,6 +1,6 @@