diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b15fd0c0f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,315 @@ +# Changelog + +## [v1.13.0](https://github.com/rocket-pool/smartnode/tree/v1.13.0) (2024-04-23) + +### Changed +- Update Geth version +- Update Besu version +- Update Lodestar version +- Update Reth version +- Updated Go to v1.21 (https://github.com/rocket-pool/smartnode/pull/476) + +### Added +- Implementation of an On-Chain pDAO. RPIP-33 +- Stake ETH on behalf of node. RPIP-32 +- RPL Withdrawal Address. RPIP-31 +- Time-based Balance and RPL Price Submissions. RPIP-35 +- Added a linter CI action to GitHub (https://github.com/rocket-pool/smartnode/pull/490) +- Added a Changelog (https://github.com/rocket-pool/smartnode/pull/504) + +### Fixed +- v8-rolling records to have the same behavior as v8 rewards (https://github.com/rocket-pool/smartnode/pull/474) +- Allow scrubbed/dissolved minipools with refunds to be closed (https://github.com/rocket-pool/smartnode/pull/487) +- Fix invalid Smart Node Update available (https://github.com/rocket-pool/smartnode-install/pull/126) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.12.1...v1.13.0) + +## [v1.12.1](https://github.com/rocket-pool/smartnode/tree/v1.12.1) (2024-04-02) + +### Fixed +- Besu version corrected + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.12.0...v1.12.1) + +## [v1.12.0](https://github.com/rocket-pool/smartnode/tree/v1.12.0) (2024-03-30) + +### Changed +- Update nethermind version +- Update Reth version +- Update Lighthouse version +- Update Teku version +- Update Prysm version +- Update Nimbus version +- Update Besu version + +### Added +- Smartnode notification functionality for bounty BA022308 (https://github.com/rocket-pool/smartnode/pull/449) + +### Fixed +- When bc status is not syncing fix progress to 1.0 (https://github.com/rocket-pool/smartnode/pull/469) +- rocketpool_node approximate rpl reward panel for RPIP-30 changes (https://github.com/rocket-pool/smartnode/pull/457) +- Support for using alerting in native mode (https://github.com/rocket-pool/smartnode/pull/465) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.9...v1.12.0) + +## [v1.11.9](https://github.com/rocket-pool/smartnode/tree/v1.11.9) (2024-03-11) + +### Changed +- Update Reth version +- Update Teku version +- Update Grafana (https://github.com/rocket-pool/smartnode/pull/456) +- Update Besu version +- Update Prysm version +- Update Lighthouse version +- Update Lodestar version + +### Added +- Add MEV-Boost version in `rocketpool service version` (https://github.com/rocket-pool/smartnode/pull/455) +- Add --all option for `rocketpool service prune` and `rocketpool service reset` per feedback in :(https://github.com/rocket-pool/smartnode/issues/323) +- Rocketpool service reset for bounty BA0902402 (https://github.com/rocket-pool/smartnode/pull/452) +- Pruning Besu if not archive node +- Check node has more than maxRPLStake on withdrawals +### Fixed +- rocketpool node stake-rpl reads incorrect selection (https://github.com/rocket-pool/smartnode/issues/450) +- Generate state manager when needed (https://github.com/rocket-pool/smartnode/pull/453) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.8...v1.11.9) + +## [v1.11.8](https://github.com/rocket-pool/smartnode/tree/v1.11.8) (2024-02-27) + +### Changed +- Update Lodestar version +- Update Geth version +- Update Reth version +- Update Besu version +- Update Teku version +- Update Nethermind version +- Update Nimbus version + +### Added +- Reth client +- Max stake fraction from contract +- Filter out duplicate pubkeys (https://github.com/rocket-pool/smartnode/pull/443) +- `MaxCollateralFraction` hardcoded to 1.5 eth (https://github.com/rocket-pool/smartnode/pull/445) +- Timeouts to rewards tree downloads (https://github.com/rocket-pool/smartnode/pull/446) +- Besu archive mdoe + +### Fixed +- "-c" shorthand for subcommands (https://github.com/rocket-pool/smartnode/pull/441) +- Voluntary exit signatures (https://github.com/rocket-pool/smartnode/pull/447) +- `rewardsFileVersion` type encapsulates version range checks. Errors more verbose (https://github.com/rocket-pool/smartnode/pull/448) + +### Removed +- Max rpl stake options + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.7...v1.11.8) + +## [v1.11.7](https://github.com/rocket-pool/smartnode/tree/v1.11.7) (2024-02-02) + +### Changed +- Update Teku version +- Update Lighthouse version +- Update Besu version +- Update Nethermind version +- Update Geth version +- Update Nimbus version +- Update Lodestar version +- Update Prysm version + +### Removed +- Removed Goerli +- Removed Prater (https://github.com/rocket-pool/smartnode/pull/437) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.6...v1.11.7) + +## [v1.11.6](https://github.com/rocket-pool/smartnode/tree/v1.11.6) (2024-01-22) + +### Changed +- Update Nethermind version +- Update Lodestar version +- Update Nethermind version + +### Added +- Test to cement CID calculation (https://github.com/rocket-pool/smartnode/pull/433) +- Not omit totalNodeWeight if not set (https://github.com/rocket-pool/smartnode/pull/433) + +### Fixed +- Go version of the Nethermind prune starter, replacing the dll (https://github.com/rocket-pool/smartnode/pull/434) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.5...v1.11.6) + +## [v1.11.5](https://github.com/rocket-pool/smartnode/tree/v1.11.5) (2024-01-13) + +### Changed +- Update Geth version +- Update Nethermind version +- Update Prysm version +- Update Besu version +- Prysm max peers increased +- Holesky to v8 on interval 93 + +### Added +- Total node weight printed in treegen logs (https://github.com/rocket-pool/smartnode/pull/431) +- Local file abstraction for treegen (https://github.com/rocket-pool/smartnode/pull/432) +- Arbitrum price messenger v2 + +### Fixed +- RR CID calculation +- Compression bugs with CID calculation (https://github.com/rocket-pool/smartnode/pull/432) +- CID filename usage + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.4...v1.11.5) + +## [v1.11.4](https://github.com/rocket-pool/smartnode/tree/v1.11.4) (2024-01-10) + +### Changed +- Update Teku version + +### Fixed +- `GetBnOpenPorts` fixed + +### Removed +- `web3.storage` refs + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.3...v1.11.4) + +## [v1.11.3](https://github.com/rocket-pool/smartnode/tree/v1.11.3) (2024-01-09) + +### Changed +- Update Besu version +- Update Nimbus version +- Update Geth version +- Update Lodestar version + +### Added +- Prompt if the user would prefer to skip key recovery when recovering wallet with unsynced clients (https://github.com/rocket-pool/smartnode/pull/425) +- Mainnet node sync check (https://github.com/rocket-pool/smartnode/issues/5) +- Treegen v8 (https://github.com/rocket-pool/smartnode/pull/424) + +### Fixed +- SNG amnesiac restart-after-stake text (https://github.com/rocket-pool/smartnode/pull/426) +- Calculating graffiti (https://github.com/rocket-pool/smartnode/pull/428) + +### Removed +- envsubst (https://github.com/rocket-pool/smartnode/pull/420) +- web3.storage dependency (https://github.com/rocket-pool/smartnode/pull/414) +- Nethermind complete history box + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.2...v1.11.3) + +## [v1.11.2](https://github.com/rocket-pool/smartnode/tree/v1.11.2) (2023-12-19) + +### Changed +- Update Teku version +- Update Nethermind version +- Continue submission when the file upload fails (https://github.com/rocket-pool/smartnode/pull/416) +- Dedup calls to LoadConfig (https://github.com/rocket-pool/smartnode/pull/418) + +### Added +- Print rescue node plugin info even when the EC is syncing (https://github.com/rocket-pool/smartnode/pull/419) +- Scroll submit price logic (https://github.com/rocket-pool/smartnode/pull/422) +- Checks to ensure if all ports in the configuration are unique (https://github.com/rocket-pool/smartnode/pull/423) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.1...v1.11.2) + +## [v1.11.1](https://github.com/rocket-pool/smartnode/tree/v1.11.1) (2023-12-05) + +### Changed +- Client versions updated (https://github.com/rocket-pool/smartnode/pull/417) + +### Added +- Prevented truncation of user-settings.yml when disk is full by writing to /tmp first and moving the result to user-settings.yml (https://github.com/rocket-pool/smartnode/pull/411) +- PBSS the default for new installations (https://github.com/rocket-pool/smartnode/pull/404) +- Add a Rescue Node add-on to make it easier for people to connect and disconnect (https://github.com/rocket-pool/smartnode/pull/402) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.11.0...v1.11.1) + +## [v1.11.0](https://github.com/rocket-pool/smartnode/tree/v1.11.0) (2023-10-12) + +### Changed +- Update Besu version +- Update Teku version +- Update Nethermind version +- Update Lodestar version +- Update Lighthouse version +- Update Nimbus version +- Update Prysm version +- Update Grafana +- Update Prometheus +- RPL node op inflation goes to the pDAO if no nodes are eligible +- Mainnet and Prater V7 intervals +- Cache size for Nethermind + +### Added +- Holesky support +- PBSS +- Rewards file v2 and accompanying interfaces +- No pruning for Geth with PBSS + +### Removed +- Cache for Geth +- Empty MPs from the index map during serialization +- Blocknative + +### Fixed +- Non-staking MPs being added to the RR cache + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.10.2...v1.11.0) + +## [v1.10.2](https://github.com/rocket-pool/smartnode/tree/v1.10.2) (2023-08-30) + +### Changed +- Update Teku version +- Update Besu version +- Update Prometheus version +- Update Geth version +- Update Prysm version +- Update Nimbus version +- Minipool distribute now sorts by most-to-least rewards and has a threshold flag + +### Added +- 200 idle HTTP connections +- Note to the daemons about the current mode + +### Removed +- Bloxroute ethical and the non-sandwiching profile + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.10.1...v1.10.2) + +## [v1.10.1](https://github.com/rocket-pool/smartnode/tree/v1.10.1) (2023-08-10) + +### Changed +- Update Lodestar version +- Update Lighthouse version + +### Added +- Arbitrary ERC20 support to node-send +- Safety checks and warnings to node-send +- Support for price submission to Base + +### Fixed +- RR issue during subsequent intervals if the first block is missing +- Missing interval rollover check for oDAO members + +### Removed +- Finalized validators from the staking count in Grafana +- Old flags from the daemon + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.10.0...v1.10.1) + +## [v1.10.0](https://github.com/rocket-pool/smartnode/tree/v1.10.0) (2023-07-24) + +### Changed +- "Expose API Port" from bool to a ternary choice (https://github.com/rocket-pool/smartnode/pull/342) +- BN client Committees response optimizations (https://github.com/rocket-pool/smartnode/pull/361) +- Slash timer messaging more explicit, ominous, and linked to the docs (https://github.com/rocket-pool/smartnode/pull/377) +- google.golang.org/grpc from 1.52.3 to 1.53.0 (https://github.com/rocket-pool/smartnode/pull/375) +- Change []beacon.Committee to an interface to reduce copying on the hot path +- Client ready checks deduplicated (https://github.com/rocket-pool/smartnode/pull/378) +- Way smartnode downloads and runs the install script to catch errors with the download better is changed (https://github.com/rocket-pool/smartnode/pull/381) +- Hoisted nonce validation, refactored to fluent for client init (https://github.com/rocket-pool/smartnode/pull/383) + +### Removed +- Removed debug line (shared/services/rewards/rolling-record.go, line #197) + +[Full Changelog](https://github.com/rocket-pool/smartnode/compare/v1.9.8...v1.10.0) \ No newline at end of file diff --git a/README.md b/README.md index 615698ec9..3627ca5ed 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ The following commands are available via the Smartnode client: - `rocketpool odao leave, l` - Leave the oracle DAO (requires an executed leave proposal) - **pdao**, p - Manage the Rocket Pool Protocol DAO - `rocketpool pdao settings, s` - Show all of the current Protocol DAO settings and values + - `rocketpool pdao voting-power, vp` - Shows the voting power of your node at the latest block - `rocketpool pdao rewards-percentages, rp` - View the RPL rewards allocation percentages for node operators, the Oracle DAO, and the Protocol DAO - `rocketpool pdao claim-bonds, cb` - Unlock any bonded RPL you have for a proposal or set of challenges, and claim any bond rewards for defending or defeating the proposal - `rocketpool pdao propose, p` - Make a Protocol DAO proposal diff --git a/client/pdao.go b/client/pdao.go index 5ececb3c4..02f815ab0 100644 --- a/client/pdao.go +++ b/client/pdao.go @@ -212,3 +212,8 @@ func (r *PDaoRequester) SetVotingDelegate(delegate common.Address) (*types.ApiRe func (r *PDaoRequester) GetCurrentVotingDelegate() (*types.ApiResponse[api.ProtocolDaoCurrentVotingDelegateData], error) { return client.SendGetRequest[api.ProtocolDaoCurrentVotingDelegateData](r, "voting-delegate", "GetCurrentVotingDelegate", nil) } + +// Get the node's voting power as of the chain head +func (r *PDaoRequester) GetVotingPower() (*types.ApiResponse[api.ProtocolDaoGetVotingPowerData], error) { + return client.SendGetRequest[api.ProtocolDaoGetVotingPowerData](r, "get-voting-power", "GetVotingPower", nil) +} diff --git a/go.mod b/go.mod index c9448268d..c056db82b 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/docker/docker v26.0.0+incompatible github.com/dustin/go-humanize v1.0.1 - github.com/ethereum/go-ethereum v1.13.14 + github.com/ethereum/go-ethereum v1.14.0 github.com/fatih/color v1.16.0 github.com/gdamore/tcell/v2 v2.7.4 github.com/go-openapi/errors v0.22.0 @@ -28,8 +28,8 @@ require ( github.com/prometheus/client_golang v1.19.0 github.com/prysmaticlabs/prysm/v5 v5.0.3 github.com/rivo/tview v0.0.0-20230208211350-7dfff1ce7854 // DO NOT UPGRADE - github.com/rocket-pool/node-manager-core v0.2.1-0.20240421193204-a16d03d086e8 - github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421193935-e15a3e374153 + github.com/rocket-pool/node-manager-core v0.2.1-0.20240424170646-136fb9278b57 + github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421200135-5ec635bb7db3 github.com/shirou/gopsutil/v3 v3.24.3 github.com/tyler-smith/go-bip39 v1.1.0 github.com/wealdtech/go-ens/v3 v3.6.0 @@ -37,8 +37,8 @@ require ( github.com/wealdtech/go-eth2-util v1.8.2 github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1 github.com/wealdtech/go-merkletree v1.0.1-0.20190605192610-2bb163c2ea2a - golang.org/x/sync v0.6.0 - golang.org/x/term v0.18.0 + golang.org/x/sync v0.7.0 + golang.org/x/term v0.19.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -58,7 +58,7 @@ require ( github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 // indirect - github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/distribution/reference v0.6.0 // indirect @@ -156,13 +156,12 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 - golang.org/x/tools v0.19.0 // indirect + golang.org/x/tools v0.20.0 // indirect google.golang.org/api v0.171.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect diff --git a/go.sum b/go.sum index e821a0323..f1c792b95 100644 --- a/go.sum +++ b/go.sum @@ -65,8 +65,8 @@ github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZ github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -84,8 +84,8 @@ github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PN github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= -github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= @@ -117,8 +117,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= -github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= +github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -467,14 +467,10 @@ github.com/rocket-pool/batch-query v1.0.0 h1:5HejmT1n1fIdLIqUhTNwbkG2PGOPl3IVjCp github.com/rocket-pool/batch-query v1.0.0/go.mod h1:d1CmxShzk0fioJ4yX0eFGhz2an1odnW/LZ2cp3eDGIQ= github.com/rocket-pool/go-merkletree v1.0.1-0.20220406020931-c262d9b976dd h1:p9KuetSKB9nte9I/MkkiM3pwKFVQgqxxPTQ0y56Ff6s= github.com/rocket-pool/go-merkletree v1.0.1-0.20220406020931-c262d9b976dd/go.mod h1:UE9fof8P7iESVtLn1K9CTSkNRYVFHZHlf96RKbU33kA= -github.com/rocket-pool/node-manager-core v0.2.1-0.20240417173109-4b54852b003a h1:hBuBMT4XT1ne/6eUdnE3BIfnMsFmFPF7pIRud0aWod8= -github.com/rocket-pool/node-manager-core v0.2.1-0.20240417173109-4b54852b003a/go.mod h1:KeVUgf+tc7e+fDUzc/FH3COtgGPoAyIV2Tx3jLN4zng= -github.com/rocket-pool/node-manager-core v0.2.1-0.20240421193204-a16d03d086e8 h1:nUHIRbYWKo+FO0zom8lpXAasNUt91iJ6k74e3fD6UvM= -github.com/rocket-pool/node-manager-core v0.2.1-0.20240421193204-a16d03d086e8/go.mod h1:KeVUgf+tc7e+fDUzc/FH3COtgGPoAyIV2Tx3jLN4zng= -github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240418131940-9aa4643f67c7 h1:liWy3ZNMhHmNOlbKfJXZbUgbBaGj4ZyEZJv+0mWaZ+k= -github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240418131940-9aa4643f67c7/go.mod h1:2IMaRByN0wfkLgNa85LA0I9oJ1QKv1nCNoffFdhF714= -github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421193935-e15a3e374153 h1:BE6SdszUN+emqc7riBJJtbTpKFeNTtt+78PX1iUVS7c= -github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421193935-e15a3e374153/go.mod h1:2IMaRByN0wfkLgNa85LA0I9oJ1QKv1nCNoffFdhF714= +github.com/rocket-pool/node-manager-core v0.2.1-0.20240424170646-136fb9278b57 h1:1cT/mGYmj/w9ZkSZ82YHq7QYSC8vnqRoGMSjWQVyur8= +github.com/rocket-pool/node-manager-core v0.2.1-0.20240424170646-136fb9278b57/go.mod h1:f/w3jnzi3ipXet7acZ0pQhGbolU/g3IDVrqXcNWuHw4= +github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421200135-5ec635bb7db3 h1:71IYwGtk3IDbTrrhTeIYJoYSCBowrU+ebacgeXI3cOs= +github.com/rocket-pool/rocketpool-go/v2 v2.0.0-20240421200135-5ec635bb7db3/go.mod h1:KgToyMtgmDo9gsmJ8KFpcpLbkETDXnPi0J8M+YmUpmA= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= @@ -601,8 +597,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -611,8 +607,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -626,8 +622,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -637,8 +633,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -667,14 +663,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -693,8 +690,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/rocketpool-cli/commands/pdao/commands.go b/rocketpool-cli/commands/pdao/commands.go index c1342379d..5ec0ce9fa 100644 --- a/rocketpool-cli/commands/pdao/commands.go +++ b/rocketpool-cli/commands/pdao/commands.go @@ -134,6 +134,16 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { }, }, + { + Name: "voting-power", + Aliases: []string{"vp"}, + Usage: "Shows the voting power of your node at the latest block", + Action: func(c *cli.Context) error { + // Run + return getVotePower(c) + }, + }, + { Name: "rewards-percentages", Aliases: []string{"rp"}, diff --git a/rocketpool-cli/commands/pdao/get-voting-power.go b/rocketpool-cli/commands/pdao/get-voting-power.go new file mode 100644 index 000000000..d158fa9db --- /dev/null +++ b/rocketpool-cli/commands/pdao/get-voting-power.go @@ -0,0 +1,25 @@ +package pdao + +import ( + "fmt" + + "github.com/rocket-pool/node-manager-core/eth" + "github.com/rocket-pool/smartnode/v2/rocketpool-cli/client" + "github.com/urfave/cli/v2" +) + +func getVotePower(c *cli.Context) error { + // Get RP client + rp := client.NewClientFromCtx(c) + + // Get node's voting power at the latest block + response, err := rp.Api.PDao.GetVotingPower() + if err != nil { + return err + } + + // Print Results + fmt.Println("== Node Voting Power ==") + fmt.Printf("Your current voting power: %.10f\n", eth.WeiToEth(response.Data.VotingPower)) + return nil +} diff --git a/rocketpool-cli/commands/pdao/invite-sc.go b/rocketpool-cli/commands/pdao/invite-sc.go index c30f40123..260eec311 100644 --- a/rocketpool-cli/commands/pdao/invite-sc.go +++ b/rocketpool-cli/commands/pdao/invite-sc.go @@ -65,6 +65,9 @@ func proposeSecurityCouncilInvite(c *cli.Context) error { if response.Data.MemberAlreadyExists { fmt.Println("The address is already part of the security council.") } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/kick-sc.go b/rocketpool-cli/commands/pdao/kick-sc.go index 35e49e3bd..87e5ae0f7 100644 --- a/rocketpool-cli/commands/pdao/kick-sc.go +++ b/rocketpool-cli/commands/pdao/kick-sc.go @@ -68,6 +68,9 @@ func proposeSecurityCouncilKick(c *cli.Context) error { if response.Data.MemberDoesNotExist { fmt.Println("The selected member does not exist.") } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/one-time-spend.go b/rocketpool-cli/commands/pdao/one-time-spend.go index 1057da28b..b3e0c2298 100644 --- a/rocketpool-cli/commands/pdao/one-time-spend.go +++ b/rocketpool-cli/commands/pdao/one-time-spend.go @@ -74,6 +74,9 @@ func proposeOneTimeSpend(c *cli.Context) error { if response.Data.InsufficientRpl { fmt.Printf("You do not have enough unlocked RPL (proposals require locking %.6f RPL, but you only have %.6f RPL staked and unlocked).", eth.WeiToEth(response.Data.ProposalBond), eth.WeiToEth(big.NewInt(0).Sub(response.Data.StakedRpl, response.Data.LockedRpl))) } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/proposals.go b/rocketpool-cli/commands/pdao/proposals.go index 5563446c9..dc70b6ed8 100644 --- a/rocketpool-cli/commands/pdao/proposals.go +++ b/rocketpool-cli/commands/pdao/proposals.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/rocketpool-go/v2/types" "github.com/urfave/cli/v2" @@ -150,11 +151,11 @@ func getProposal(c *cli.Context, id uint64) error { } // Vote details - fmt.Printf("Voting power required: %s\n", proposal.VotingPowerRequired.String()) - fmt.Printf("Voting power for: %s\n", proposal.VotingPowerFor.String()) - fmt.Printf("Voting power against: %s\n", proposal.VotingPowerAgainst.String()) - fmt.Printf("Voting power abstained: %s\n", proposal.VotingPowerAbstained.String()) - fmt.Printf("Voting power against: %s\n", proposal.VotingPowerToVeto.String()) + fmt.Printf("Voting power required: %.10f\n", eth.WeiToEth(proposal.VotingPowerRequired)) + fmt.Printf("Voting power for: %.10f\n", eth.WeiToEth(proposal.VotingPowerFor)) + fmt.Printf("Voting power against: %.10f\n", eth.WeiToEth(proposal.VotingPowerAgainst)) + fmt.Printf("Voting power abstained: %.10f\n", eth.WeiToEth(proposal.VotingPowerAbstained)) + fmt.Printf("Voting power against: %.10f\n", eth.WeiToEth(proposal.VotingPowerToVeto)) if proposal.NodeVoteDirection != types.VoteDirection_NoVote { fmt.Printf("Node has voted: %s\n", types.VoteDirections[proposal.NodeVoteDirection]) } else { diff --git a/rocketpool-cli/commands/pdao/propose-rewards-percentages.go b/rocketpool-cli/commands/pdao/propose-rewards-percentages.go index 5210bd4dd..c02af23dd 100644 --- a/rocketpool-cli/commands/pdao/propose-rewards-percentages.go +++ b/rocketpool-cli/commands/pdao/propose-rewards-percentages.go @@ -89,6 +89,9 @@ func proposeRewardsPercentages(c *cli.Context) error { if response.Data.InsufficientRpl { fmt.Printf("You do not have enough unlocked RPL (proposals require locking %.6f RPL, but you only have %.6f RPL staked and unlocked).", eth.WeiToEth(response.Data.ProposalBond), eth.WeiToEth(big.NewInt(0).Sub(response.Data.StakedRpl, response.Data.LockedRpl))) } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/propose-setting.go b/rocketpool-cli/commands/pdao/propose-setting.go index 6efaf9201..e68e2aeaf 100644 --- a/rocketpool-cli/commands/pdao/propose-setting.go +++ b/rocketpool-cli/commands/pdao/propose-setting.go @@ -51,6 +51,9 @@ func proposeSetting[ValueType utils.SettingType](c *cli.Context, contract rocket if response.Data.UnknownSetting { fmt.Printf("Unknown setting '%s' on contract '%s'.\n", setting, contract) } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/recurring-spend-update.go b/rocketpool-cli/commands/pdao/recurring-spend-update.go index 508420da6..2b2b521c7 100644 --- a/rocketpool-cli/commands/pdao/recurring-spend-update.go +++ b/rocketpool-cli/commands/pdao/recurring-spend-update.go @@ -86,6 +86,9 @@ func proposeRecurringSpendUpdate(c *cli.Context) error { if response.Data.InsufficientRpl { fmt.Printf("You do not have enough unlocked RPL (proposals require locking %.6f RPL, but you only have %.6f RPL staked and unlocked).", eth.WeiToEth(response.Data.ProposalBond), eth.WeiToEth(big.NewInt(0).Sub(response.Data.StakedRpl, response.Data.LockedRpl))) } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/recurring-spend.go b/rocketpool-cli/commands/pdao/recurring-spend.go index e351af6a1..8fa2e3f86 100644 --- a/rocketpool-cli/commands/pdao/recurring-spend.go +++ b/rocketpool-cli/commands/pdao/recurring-spend.go @@ -108,6 +108,9 @@ func proposeRecurringSpend(c *cli.Context) error { if response.Data.InsufficientRpl { fmt.Printf("You do not have enough unlocked RPL (proposals require locking %.6f RPL, but you only have %.6f RPL staked and unlocked).", eth.WeiToEth(response.Data.ProposalBond), eth.WeiToEth(big.NewInt(0).Sub(response.Data.StakedRpl, response.Data.LockedRpl))) } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/pdao/replace-sc.go b/rocketpool-cli/commands/pdao/replace-sc.go index 28457a05f..bd3971e7e 100644 --- a/rocketpool-cli/commands/pdao/replace-sc.go +++ b/rocketpool-cli/commands/pdao/replace-sc.go @@ -109,6 +109,9 @@ func proposeSecurityCouncilReplace(c *cli.Context) error { if response.Data.OldMemberDoesNotExist { fmt.Println("The existing address is not a member of the security council.") } + if response.Data.IsRplLockingDisallowed { + fmt.Println("Please enable RPL locking using the command 'rocketpool node allow-rpl-locking' to raise proposals.") + } return nil } diff --git a/rocketpool-cli/commands/service/commands.go b/rocketpool-cli/commands/service/commands.go index 3d99cc57f..6e97f59f4 100644 --- a/rocketpool-cli/commands/service/commands.go +++ b/rocketpool-cli/commands/service/commands.go @@ -85,6 +85,12 @@ func createFlagsFromConfigParams(prefix string, section config.IConfigSection, c Usage: fmt.Sprintf("%s\n\tType: uint16\n", description), Value: uint(uint16Param.GetDefault(network)), }) + } else if int64Param, ok := param.(*config.Parameter[int64]); ok { + configFlags = append(configFlags, &cli.Int64Flag{ + Name: paramName, + Usage: fmt.Sprintf("%s\n\tType: int64\n", description), + Value: int64Param.GetDefault(network), + }) } else { panic(fmt.Sprintf("param [%s] is not a supported type for form item binding", paramName)) } diff --git a/rocketpool-cli/commands/service/config.go b/rocketpool-cli/commands/service/config.go index e4941c0a2..1fe86cde7 100644 --- a/rocketpool-cli/commands/service/config.go +++ b/rocketpool-cli/commands/service/config.go @@ -198,6 +198,8 @@ func updateConfigParamsFromCliArgs(c *cli.Context, prefix string, section nmc_co uintParam.Value = c.Uint64(paramName) } else if uint16Param, ok := param.(*nmc_config.Parameter[uint16]); ok { uint16Param.Value = uint16(c.Uint(paramName)) + } else if int64Param, ok := param.(*nmc_config.Parameter[int64]); ok { + int64Param.Value = c.Int64(paramName) } else { panic(fmt.Sprintf("param [%s] is not a supported type for form item binding", paramName)) } diff --git a/rocketpool-cli/commands/service/config/cfg-form.go b/rocketpool-cli/commands/service/config/cfg-form.go index 3898dbc84..bf42ff693 100644 --- a/rocketpool-cli/commands/service/config/cfg-form.go +++ b/rocketpool-cli/commands/service/config/cfg-form.go @@ -53,6 +53,9 @@ func getTypedFormItem(param config.IParameter, descriptionBox *tview.TextView) * if intParam, ok := param.(*config.Parameter[int]); ok { return createParameterizedIntField(intParam) } + if int64Param, ok := param.(*config.Parameter[int64]); ok { + return createParameterizedInt64Field(int64Param) + } if uintParam, ok := param.(*config.Parameter[uint64]); ok { return createParameterizedUintField(uintParam) } @@ -128,6 +131,41 @@ func createParameterizedIntField(param *config.Parameter[int]) *parameterizedFor } } +// Create a standard int64 field +func createParameterizedInt64Field(param *config.Parameter[int64]) *parameterizedFormItem { + item := tview.NewInputField(). + SetLabel(param.Name). + SetAcceptanceFunc(tview.InputFieldInteger) + item.SetDoneFunc(func(key tcell.Key) { + if key == tcell.KeyEscape { + item.SetText("") + } else { + value, err := strconv.ParseInt(item.GetText(), 0, 0) + if err != nil { + // TODO: show error modal? + item.SetText("") + } else { + param.Value = value + } + } + }) + item.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyDown, tcell.KeyTab: + return tcell.NewEventKey(tcell.KeyTab, 0, 0) + case tcell.KeyUp, tcell.KeyBacktab: + return tcell.NewEventKey(tcell.KeyBacktab, 0, 0) + default: + return event + } + }) + + return ¶meterizedFormItem{ + parameter: param, + item: item, + } +} + // Create a standard uint field func createParameterizedUintField(param *config.Parameter[uint64]) *parameterizedFormItem { item := tview.NewInputField(). diff --git a/rocketpool-daemon/api/pdao/get-voting-power.go b/rocketpool-daemon/api/pdao/get-voting-power.go new file mode 100644 index 000000000..74f53560f --- /dev/null +++ b/rocketpool-daemon/api/pdao/get-voting-power.go @@ -0,0 +1,80 @@ +package pdao + +import ( + "fmt" + "net/url" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gorilla/mux" + batch "github.com/rocket-pool/batch-query" + "github.com/rocket-pool/rocketpool-go/v2/node" + + "github.com/rocket-pool/node-manager-core/api/server" + "github.com/rocket-pool/node-manager-core/api/types" + "github.com/rocket-pool/smartnode/v2/shared/types/api" +) + +// =============== +// === Factory === +// =============== + +type protocolDaoGetVotingPowerContextFactory struct { + handler *ProtocolDaoHandler +} + +func (f *protocolDaoGetVotingPowerContextFactory) Create(args url.Values) (*protocolDaoGetVotingPowerContext, error) { + c := &protocolDaoGetVotingPowerContext{ + handler: f.handler, + } + return c, nil +} + +func (f *protocolDaoGetVotingPowerContextFactory) RegisterRoute(router *mux.Router) { + server.RegisterQuerylessGet[*protocolDaoGetVotingPowerContext, api.ProtocolDaoGetVotingPowerData]( + router, "get-voting-power", f, f.handler.logger.Logger, f.handler.serviceProvider.ServiceProvider, + ) +} + +// =============== +// === Context === +// =============== + +type protocolDaoGetVotingPowerContext struct { + handler *ProtocolDaoHandler +} + +func (c *protocolDaoGetVotingPowerContext) PrepareData(data *api.ProtocolDaoGetVotingPowerData, opts *bind.TransactOpts) (types.ResponseStatus, error) { + sp := c.handler.serviceProvider + rp := sp.GetRocketPool() + ec := sp.GetEthClient() + + nodeAddress, _ := sp.GetWallet().GetAddress() + + // Requirements + status, err := sp.RequireNodeRegistered(c.handler.ctx) + if err != nil { + return status, err + } + + // Bindings + node, err := node.NewNode(rp, nodeAddress) + if err != nil { + return types.ResponseStatus_Error, fmt.Errorf("error creating node %s binding: %w", nodeAddress.Hex(), err) + } + + // Get the latest block + blockNumber, err := ec.BlockNumber(c.handler.ctx) + if err != nil { + return types.ResponseStatus_Error, fmt.Errorf("error getting latest block number: %w", err) + } + + // Get the voting power at that block + err = rp.Query(func(mc *batch.MultiCaller) error { + node.GetVotingPowerAtBlock(mc, &data.VotingPower, uint32(blockNumber)) + return nil + }, nil) + if err != nil { + return types.ResponseStatus_Error, fmt.Errorf("error getting voting power for block %d: %w", blockNumber, err) + } + return types.ResponseStatus_Success, nil +} diff --git a/rocketpool-daemon/api/pdao/handler.go b/rocketpool-daemon/api/pdao/handler.go index 9ccecdf87..48a2dd5dc 100644 --- a/rocketpool-daemon/api/pdao/handler.go +++ b/rocketpool-daemon/api/pdao/handler.go @@ -46,6 +46,7 @@ func NewProtocolDaoHandler(logger *log.Logger, ctx context.Context, serviceProvi &protocolDaoInitializeVotingContextFactory{h}, &protocolDaoSetVotingDelegateContextFactory{h}, &protocolDaoCurrentVotingDelegateContextFactory{h}, + &protocolDaoGetVotingPowerContextFactory{h}, } return h } diff --git a/rocketpool-daemon/api/pdao/invite-security.go b/rocketpool-daemon/api/pdao/invite-security.go index 4ee57db6c..bd0859fc4 100644 --- a/rocketpool-daemon/api/pdao/invite-security.go +++ b/rocketpool-daemon/api/pdao/invite-security.go @@ -101,6 +101,7 @@ func (c *protocolDaoProposeInviteToSecurityCouncilContext) GetState(mc *batch.Mu c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -109,10 +110,11 @@ func (c *protocolDaoProposeInviteToSecurityCouncilContext) PrepareData(data *api data.MemberAlreadyExists = c.member.Exists.Get() data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.MemberAlreadyExists || data.InsufficientRpl) + data.CanPropose = !(data.MemberAlreadyExists || data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/kick-security.go b/rocketpool-daemon/api/pdao/kick-security.go index 530dd0030..25113061e 100644 --- a/rocketpool-daemon/api/pdao/kick-security.go +++ b/rocketpool-daemon/api/pdao/kick-security.go @@ -100,6 +100,7 @@ func (c *protocolDaoProposeKickFromSecurityCouncilContext) GetState(mc *batch.Mu c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -108,10 +109,11 @@ func (c *protocolDaoProposeKickFromSecurityCouncilContext) PrepareData(data *api data.MemberDoesNotExist = !c.member.Exists.Get() data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.MemberDoesNotExist || data.InsufficientRpl) + data.CanPropose = !(data.MemberDoesNotExist || data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/one-time-spend.go b/rocketpool-daemon/api/pdao/one-time-spend.go index 62910d238..bb168fce0 100644 --- a/rocketpool-daemon/api/pdao/one-time-spend.go +++ b/rocketpool-daemon/api/pdao/one-time-spend.go @@ -96,6 +96,7 @@ func (c *protocolDaoProposeOneTimeSpendContext) GetState(mc *batch.MultiCaller) c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -103,10 +104,11 @@ func (c *protocolDaoProposeOneTimeSpendContext) PrepareData(data *api.ProtocolDa ctx := c.handler.ctx data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.InsufficientRpl) + data.CanPropose = !(data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/propose-rewards-percentages.go b/rocketpool-daemon/api/pdao/propose-rewards-percentages.go index 33501a17d..14d205619 100644 --- a/rocketpool-daemon/api/pdao/propose-rewards-percentages.go +++ b/rocketpool-daemon/api/pdao/propose-rewards-percentages.go @@ -96,6 +96,7 @@ func (c *protocolDaoProposeRewardsPercentagesContext) GetState(mc *batch.MultiCa c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -112,10 +113,11 @@ func (c *protocolDaoProposeRewardsPercentagesContext) PrepareData(data *api.Prot data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.InsufficientRpl) + data.CanPropose = !(data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/propose-settings.go b/rocketpool-daemon/api/pdao/propose-settings.go index 680e57502..67738cb3a 100644 --- a/rocketpool-daemon/api/pdao/propose-settings.go +++ b/rocketpool-daemon/api/pdao/propose-settings.go @@ -96,12 +96,14 @@ func (c *protocolDaoProposeSettingContext) GetState(mc *batch.MultiCaller) { c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } func (c *protocolDaoProposeSettingContext) PrepareData(data *api.ProtocolDaoProposeSettingData, opts *bind.TransactOpts) (types.ResponseStatus, error) { data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) @@ -112,7 +114,7 @@ func (c *protocolDaoProposeSettingContext) PrepareData(data *api.ProtocolDaoProp if !exists { data.UnknownSetting = true } - data.CanPropose = !(data.InsufficientRpl || data.UnknownSetting) + data.CanPropose = !(data.InsufficientRpl || data.UnknownSetting || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/recurring-spend.go b/rocketpool-daemon/api/pdao/recurring-spend.go index df77d9a95..082752c9f 100644 --- a/rocketpool-daemon/api/pdao/recurring-spend.go +++ b/rocketpool-daemon/api/pdao/recurring-spend.go @@ -103,6 +103,7 @@ func (c *protocolDaoProposeRecurringSpendContext) GetState(mc *batch.MultiCaller c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -110,10 +111,11 @@ func (c *protocolDaoProposeRecurringSpendContext) PrepareData(data *api.Protocol ctx := c.handler.ctx data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.InsufficientRpl) + data.CanPropose = !(data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/replace-security.go b/rocketpool-daemon/api/pdao/replace-security.go index fb6c396ed..2c8649f34 100644 --- a/rocketpool-daemon/api/pdao/replace-security.go +++ b/rocketpool-daemon/api/pdao/replace-security.go @@ -110,6 +110,7 @@ func (c *protocolDaoProposeReplaceMemberOfSecurityCouncilContext) GetState(mc *b c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) } @@ -119,10 +120,11 @@ func (c *protocolDaoProposeReplaceMemberOfSecurityCouncilContext) PrepareData(da data.OldMemberDoesNotExist = !c.existingMember.Exists.Get() data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.NewMemberAlreadyExists || data.OldMemberDoesNotExist || data.InsufficientRpl) + data.CanPropose = !(data.NewMemberAlreadyExists || data.OldMemberDoesNotExist || data.InsufficientRpl || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/api/pdao/update-recurring-spend.go b/rocketpool-daemon/api/pdao/update-recurring-spend.go index 3473870cc..0f9eadbbd 100644 --- a/rocketpool-daemon/api/pdao/update-recurring-spend.go +++ b/rocketpool-daemon/api/pdao/update-recurring-spend.go @@ -102,6 +102,7 @@ func (c *protocolDaoProposeRecurringSpendUpdateContext) GetState(mc *batch.Multi c.pdaoMgr.Settings.Proposals.ProposalBond, c.node.RplLocked, c.node.RplStake, + c.node.IsRplLockingAllowed, ) c.pdaoMgr.GetContractExists(mc, &c.contractExists, c.contractName) } @@ -111,10 +112,11 @@ func (c *protocolDaoProposeRecurringSpendUpdateContext) PrepareData(data *api.Pr data.DoesNotExist = !c.contractExists data.StakedRpl = c.node.RplStake.Get() data.LockedRpl = c.node.RplLocked.Get() + data.IsRplLockingDisallowed = !c.node.IsRplLockingAllowed.Get() data.ProposalBond = c.pdaoMgr.Settings.Proposals.ProposalBond.Get() unlockedRpl := big.NewInt(0).Sub(data.StakedRpl, data.LockedRpl) data.InsufficientRpl = (unlockedRpl.Cmp(data.ProposalBond) < 0) - data.CanPropose = !(data.InsufficientRpl || data.DoesNotExist) + data.CanPropose = !(data.InsufficientRpl || data.DoesNotExist || data.IsRplLockingDisallowed) // Get the tx if data.CanPropose && opts != nil { diff --git a/rocketpool-daemon/watchtower/submit-network-balances.go b/rocketpool-daemon/watchtower/submit-network-balances.go index 23b0bfb30..a588229d7 100644 --- a/rocketpool-daemon/watchtower/submit-network-balances.go +++ b/rocketpool-daemon/watchtower/submit-network-balances.go @@ -15,7 +15,6 @@ import ( batch "github.com/rocket-pool/batch-query" "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/rocketpool-go/v2/network" - "github.com/rocket-pool/rocketpool-go/v2/rewards" "github.com/rocket-pool/rocketpool-go/v2/rocketpool" rptypes "github.com/rocket-pool/rocketpool-go/v2/types" rpstate "github.com/rocket-pool/rocketpool-go/v2/utils/state" @@ -101,97 +100,50 @@ func (t *SubmitNetworkBalances) Run(state *state.NetworkState) error { t.logger.Info("Starting network balance check.") // Check the last submission block - lastSubmissionBlock := state.NetworkDetails.BalancesBlock.Uint64() - networkMgr, err := network.NewNetworkManager(t.rp) - if err != nil { - return fmt.Errorf("error creating network manager binding: %w", err) - } - - // Get the last balances updated event - found, event, err := networkMgr.GetBalancesUpdatedEvent(lastSubmissionBlock, t.eventLogInterval, nil, nil) - if err != nil { - return fmt.Errorf("error getting event for balances updated on block %d: %w", lastSubmissionBlock, err) - } + lastSubmissionBlock := state.NetworkDetails.PricesBlock + referenceTimestamp := t.cfg.PriceBalanceSubmissionReferenceTimestamp.Value // Get the duration in seconds for the interval between submissions - submissionIntervalDuration := time.Duration(state.NetworkDetails.BalancesSubmissionFrequency * uint64(time.Second)) + submissionIntervalInSeconds := int64(state.NetworkDetails.PricesSubmissionFrequency) eth2Config := state.BeaconConfig - t.logger.Debug("Got last submission block and interval duration.", - slog.Uint64(keys.BlockKey, lastSubmissionBlock), - slog.Duration(keys.IntervalKey, submissionIntervalDuration), - ) - - var nextSubmissionTime time.Time - if !found { - // The first submission after Houston is deployed won't find an event emitted by this contract - // The submission time will be adjusted to align with the reward time - rewardsPool, err := rewards.NewRewardsPool(t.rp) - if err != nil { - return fmt.Errorf("error creating rewards pool binding: %w", err) - } - err = t.rp.Query(nil, nil, - rewardsPool.IntervalStart, - rewardsPool.IntervalDuration, - ) - if err != nil { - return fmt.Errorf("error getting rewards pool interval details: %w", err) - } - lastCheckpoint := rewardsPool.IntervalStart.Formatted() - rewardsInterval := rewardsPool.IntervalDuration.Formatted() - - // Find the next checkpoint - nextCheckpoint := lastCheckpoint.Add(rewardsInterval) - - // Calculate the number of submissions between now and the next checkpoint adding one so we have the first submission time that is in the past - timeDifference := time.Until(nextCheckpoint) - submissionsUntilNextCheckpoint := int(timeDifference/submissionIntervalDuration) + 1 - - nextSubmissionTime = nextCheckpoint.Add(-time.Duration(submissionsUntilNextCheckpoint) * submissionIntervalDuration) - t.logger.Debug("Balances updated event not found, using rewards pool", - slog.Time(keys.StartKey, lastCheckpoint), - slog.Duration(keys.IntervalKey, rewardsInterval), - slog.Time(keys.NextKey, nextCheckpoint), - ) - } else { - // Get the last submission reference time - lastSubmissionTime := event.SlotTimestamp - - // Next submission adds the interval time to the last submission time - nextSubmissionTime = lastSubmissionTime.Add(submissionIntervalDuration) - - t.logger.Debug("Found balances updated event", - slog.Uint64(keys.SubmittedKey, event.BlockNumber), - ) + // Get the time of the latest block + latestEth1Block, err := t.rp.Client.HeaderByNumber(context.Background(), nil) + if err != nil { + return fmt.Errorf("Can't get the latest block time: %w", err) } + latestBlockTimestamp := int64(latestEth1Block.Time) - t.logger.Debug("Checking next submission time", - slog.Time(keys.TimeKey, time.Now().UTC()), - slog.Time(keys.NextKey, nextSubmissionTime), - ) - - // Return if the time to submit has not arrived - if time.Now().Before(nextSubmissionTime) { - return nil + // Calculate the next submission timestamp + submissionTimestamp, err := utils.FindNextSubmissionTimestamp(latestBlockTimestamp, referenceTimestamp, submissionIntervalInSeconds) + if err != nil { + return err } + // Convert the submission timestamp to time.Time + nextSubmissionTime := time.Unix(submissionTimestamp, 0) + // Get the Beacon block corresponding to this time genesisTime := time.Unix(int64(eth2Config.GenesisTime), 0) timeSinceGenesis := nextSubmissionTime.Sub(genesisTime) slotNumber := uint64(timeSinceGenesis.Seconds()) / eth2Config.SecondsPerSlot // Search for the last existing EL block, going back up to 32 slots if the block is not found. - ecBlock, err := utils.FindLastExistingELBlockFromSlot(t.ctx, t.bc, slotNumber) + targetBlock, err := utils.FindLastBlockWithExecutionPayload(t.ctx, t.bc, slotNumber) if err != nil { return err } - // Fetch the target block - targetBlockHeader, err := t.ec.HeaderByHash(t.ctx, ecBlock.BlockHash) + targetBlockNumber := targetBlock.ExecutionBlockNumber + if targetBlockNumber <= lastSubmissionBlock { + // No submission needed: target block older or equal to the last submission + return nil + } + + targetBlockHeader, err := t.ec.HeaderByNumber(context.Background(), big.NewInt(int64(targetBlockNumber))) if err != nil { return err } - blockNumber := targetBlockHeader.Number.Uint64() // Check if the required epoch is finalized yet requiredEpoch := slotNumber / eth2Config.SlotsPerEpoch @@ -201,7 +153,7 @@ func (t *SubmitNetworkBalances) Run(state *state.NetworkState) error { } finalizedEpoch := beaconHead.FinalizedEpoch if requiredEpoch > finalizedEpoch { - t.logger.Info("Balance report is due, waiting for target epoch to finalize.", slog.Uint64(keys.BlockKey, blockNumber), slog.Uint64(keys.TargetEpochKey, requiredEpoch), slog.Uint64(keys.FinalizedEpochKey, finalizedEpoch)) + t.logger.Info("Balance report is due, waiting for target epoch to finalize.", slog.Uint64(keys.BlockKey, targetBlockNumber), slog.Uint64(keys.TargetEpochKey, requiredEpoch), slog.Uint64(keys.FinalizedEpochKey, finalizedEpoch)) return nil } @@ -223,7 +175,7 @@ func (t *SubmitNetworkBalances) Run(state *state.NetworkState) error { t.logger.Info("Starting balance report in a separate thread.") // Log - t.logger.Info("Calculating network balances...", slog.Uint64(keys.BlockKey, blockNumber)) + t.logger.Info("Calculating network balances...", slog.Uint64(keys.BlockKey, targetBlockNumber)) // Get network balances at block balances, err := t.getNetworkBalances(targetBlockHeader, targetBlockHeader.Number, slotNumber, time.Unix(int64(targetBlockHeader.Time), 0)) @@ -244,7 +196,7 @@ func (t *SubmitNetworkBalances) Run(state *state.NetworkState) error { // Check if we have reported these specific values before balances.SlotTimestamp = uint64(nextSubmissionTime.Unix()) - hasSubmittedSpecific, err := t.hasSubmittedSpecificBlockBalances(nodeAddress, blockNumber, balances) + hasSubmittedSpecific, err := t.hasSubmittedSpecificBlockBalances(nodeAddress, targetBlockNumber, balances) if err != nil { t.handleError(err) return @@ -257,13 +209,13 @@ func (t *SubmitNetworkBalances) Run(state *state.NetworkState) error { } // We haven't submitted these values, check if we've submitted any for this block so we can log it - hasSubmitted, err := t.hasSubmittedBlockBalances(nodeAddress, blockNumber) + hasSubmitted, err := t.hasSubmittedBlockBalances(nodeAddress, targetBlockNumber) if err != nil { t.handleError(err) return } if hasSubmitted { - t.logger.Info("Have previously submitted out-of-date balances, trying again...", slog.Uint64(keys.BlockKey, blockNumber)) + t.logger.Info("Have previously submitted out-of-date balances, trying again...", slog.Uint64(keys.BlockKey, targetBlockNumber)) } // Log diff --git a/rocketpool-daemon/watchtower/submit-rewards-tree-stateless.go b/rocketpool-daemon/watchtower/submit-rewards-tree-stateless.go index d8c86e026..f09e15c5c 100644 --- a/rocketpool-daemon/watchtower/submit-rewards-tree-stateless.go +++ b/rocketpool-daemon/watchtower/submit-rewards-tree-stateless.go @@ -123,7 +123,30 @@ func (t *SubmitRewardsTree_Stateless) Run(nodeTrusted bool, state *state.Network } // Get the block and timestamp of the consensus block that best matches the end time - snapshotBeaconBlock, elBlockNumber, err := t.getSnapshotConsensusBlock(endTime, state) + snapshotBeaconBlock := t.getTargetSlot(endTime, state) + + // Get the beacon head + beaconHead, err := t.bc.GetBeaconHead(t.ctx) + if err != nil { + return fmt.Errorf("error getting Beacon head: %w", err) + } + eth2Config := state.BeaconConfig + targetSlotEpoch := snapshotBeaconBlock / eth2Config.SlotsPerEpoch + requiredEpoch := targetSlotEpoch + 1 // The smoothing pool requires 1 epoch beyond the target to be finalized, to check for late attestations + + // Check if the required epoch is finalized yet + if beaconHead.FinalizedEpoch < requiredEpoch { + t.logger.Info("Rewards are due, waiting until target epoch is finalized.", + slog.Time(keys.EndKey, endTime), + slog.Uint64(keys.TargetSlotKey, snapshotBeaconBlock), + slog.Uint64(keys.TargetEpochKey, targetSlotEpoch), + slog.Uint64(keys.RequiredEpochKey, requiredEpoch), + slog.Uint64(keys.FinalizedEpochKey, beaconHead.FinalizedEpoch), + ) + return nil + } + + elBlockNumber, err := t.getSnapshotExecutionBlock(snapshotBeaconBlock, state) if err != nil { return err } @@ -445,13 +468,7 @@ func (t *SubmitRewardsTree_Stateless) submitRewardsSnapshot(index *big.Int, cons } // Get the first finalized, successful consensus block that occurred after the given target time -func (t *SubmitRewardsTree_Stateless) getSnapshotConsensusBlock(endTime time.Time, state *state.NetworkState) (uint64, uint64, error) { - // Get the beacon head - beaconHead, err := t.bc.GetBeaconHead(t.ctx) - if err != nil { - return 0, 0, fmt.Errorf("error getting Beacon head: %w", err) - } - +func (t *SubmitRewardsTree_Stateless) getTargetSlot(endTime time.Time, state *state.NetworkState) uint64 { // Get the target block number eth2Config := state.BeaconConfig genesisTime := time.Unix(int64(eth2Config.GenesisTime), 0) @@ -459,19 +476,17 @@ func (t *SubmitRewardsTree_Stateless) getSnapshotConsensusBlock(endTime time.Tim targetSlot := uint64(math.Ceil(totalTimespan.Seconds() / float64(eth2Config.SecondsPerSlot))) targetSlotEpoch := targetSlot / eth2Config.SlotsPerEpoch targetSlot = targetSlotEpoch*eth2Config.SlotsPerEpoch + (eth2Config.SlotsPerEpoch - 1) // The target slot becomes the last one in the Epoch - requiredEpoch := targetSlotEpoch + 1 // The smoothing pool requires 1 epoch beyond the target to be finalized, to check for late attestations - - // Check if the required epoch is finalized yet - if beaconHead.FinalizedEpoch < requiredEpoch { - return 0, 0, fmt.Errorf("snapshot end time = %s, slot (epoch) = %d (%d)... waiting until epoch %d is finalized (currently %d)", endTime, targetSlot, targetSlotEpoch, requiredEpoch, beaconHead.FinalizedEpoch) - } + return targetSlot +} +// Get the first finalized, successful consensus block that occurred after the given target time +func (t *SubmitRewardsTree_Stateless) getSnapshotExecutionBlock(targetSlot uint64, state *state.NetworkState) (uint64, error) { // Get the first successful block for { // Try to get the current block block, exists, err := t.bc.GetBeaconBlock(t.ctx, fmt.Sprint(targetSlot)) if err != nil { - return 0, 0, fmt.Errorf("error getting Beacon block %d: %w", targetSlot, err) + return 0, fmt.Errorf("error getting Beacon block %d: %w", targetSlot, err) } // If the block was missing, try the previous one @@ -480,7 +495,7 @@ func (t *SubmitRewardsTree_Stateless) getSnapshotConsensusBlock(endTime time.Tim targetSlot-- } else { // Ok, we have the first proposed finalized block - this is the one to use for the snapshot! - return targetSlot, block.ExecutionBlockNumber, nil + return block.ExecutionBlockNumber, nil } } } diff --git a/rocketpool-daemon/watchtower/submit-rpl-price.go b/rocketpool-daemon/watchtower/submit-rpl-price.go index aae7c5f18..538f626f6 100644 --- a/rocketpool-daemon/watchtower/submit-rpl-price.go +++ b/rocketpool-daemon/watchtower/submit-rpl-price.go @@ -7,7 +7,6 @@ import ( "log/slog" "math/big" "sync" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -15,7 +14,6 @@ import ( batch "github.com/rocket-pool/batch-query" "github.com/rocket-pool/node-manager-core/eth" "github.com/rocket-pool/rocketpool-go/v2/network" - "github.com/rocket-pool/rocketpool-go/v2/rewards" "github.com/rocket-pool/rocketpool-go/v2/rocketpool" "github.com/rocket-pool/node-manager-core/beacon" @@ -88,95 +86,52 @@ func (t *SubmitRplPrice) Run(state *state.NetworkState) error { t.logger.Error("Error updating L2 prices", log.Err(err)) } - // Make a new RP binding just for this portion - rp := t.sp.GetRocketPool() - // Check the last submission block - lastSubmissionBlock := state.NetworkDetails.BalancesBlock.Uint64() - networkMgr, err := network.NewNetworkManager(rp) - if err != nil { - return fmt.Errorf("error creating network manager binding: %w", err) - } - - // Get the last prices updated event - found, event, err := networkMgr.GetPriceUpdatedEvent(lastSubmissionBlock, t.eventLogInterval, nil, nil) - if err != nil { - return fmt.Errorf("error getting event for price updated on block %d: %w", lastSubmissionBlock, err) - } + lastSubmissionBlock := state.NetworkDetails.PricesBlock + referenceTimestamp := t.cfg.PriceBalanceSubmissionReferenceTimestamp.Value // Get the duration in seconds for the interval between submissions - submissionIntervalDuration := time.Duration(state.NetworkDetails.PricesSubmissionFrequency * uint64(time.Second)) + submissionIntervalInSeconds := int64(state.NetworkDetails.PricesSubmissionFrequency) eth2Config := state.BeaconConfig - var nextSubmissionTime time.Time - if !found { - // The first submission after Houston is deployed won't find an event emitted by this contract - // The submission time will be adjusted to align with the reward time - rewardsPool, err := rewards.NewRewardsPool(rp) - if err != nil { - return fmt.Errorf("error creating rewards pool binding: %w", err) - } - err = rp.Query(nil, nil, - rewardsPool.IntervalStart, - rewardsPool.IntervalDuration, - ) - if err != nil { - return fmt.Errorf("error getting rewards pool interval details: %w", err) - } - lastCheckpoint := rewardsPool.IntervalStart.Formatted() - rewardsInterval := rewardsPool.IntervalDuration.Formatted() - - // Find the next checkpoint - nextCheckpoint := lastCheckpoint.Add(rewardsInterval) - - // Calculate the number of submissions between now and the next checkpoint adding one so we have the first submission time that is in the past - timeDifference := time.Until(nextCheckpoint) - submissionsUntilNextCheckpoint := int(timeDifference/submissionIntervalDuration) + 1 - - nextSubmissionTime = nextCheckpoint.Add(-time.Duration(submissionsUntilNextCheckpoint) * submissionIntervalDuration) - } else { - // Get the last submission reference time - lastSubmissionTime := event.SlotTimestamp - - // Next submission adds the interval time to the last submission time - nextSubmissionTime = lastSubmissionTime.Add(submissionIntervalDuration) + // Get the time of the latest block + latestEth1Block, err := t.rp.Client.HeaderByNumber(context.Background(), nil) + if err != nil { + return fmt.Errorf("Can't get the latest block time: %w", err) } + latestBlockTimestamp := int64(latestEth1Block.Time) - // Return if the time to submit has not arrived - if time.Now().Before(nextSubmissionTime) { - return nil + // Calculate the next submission timestamp + submissionTimestamp, err := utils.FindNextSubmissionTimestamp(latestBlockTimestamp, referenceTimestamp, submissionIntervalInSeconds) + if err != nil { + return err } - // Log - t.logger.Info("Starting RPL price report check.") - - // Get the Beacon block corresponding to this time - genesisTime := time.Unix(int64(eth2Config.GenesisTime), 0) - timeSinceGenesis := nextSubmissionTime.Sub(genesisTime) - slotNumber := uint64(timeSinceGenesis.Seconds()) / eth2Config.SecondsPerSlot + // Get the Beacon slot corresponding to this time + genesisTime := (int64(eth2Config.GenesisTime)) + timeSinceGenesis := submissionTimestamp - genesisTime + slotNumber := uint64(timeSinceGenesis) / eth2Config.SecondsPerSlot // Search for the last existing EL block, going back up to 32 slots if the block is not found. - ecBlock, err := utils.FindLastExistingELBlockFromSlot(t.ctx, t.bc, slotNumber) + targetBlock, err := utils.FindLastBlockWithExecutionPayload(t.ctx, t.bc, slotNumber) if err != nil { return err } - - // Fetch the target block - targetBlockHeader, err := t.ec.HeaderByHash(context.Background(), ecBlock.BlockHash) - if err != nil { - return err + targetBlockNumber := targetBlock.ExecutionBlockNumber + if targetBlockNumber <= lastSubmissionBlock { + // No submission needed: target block older or equal to the last submission + return nil } - blockNumber := targetBlockHeader.Number.Uint64() // Check if the required epoch is finalized yet - requiredEpoch := slotNumber / eth2Config.SlotsPerEpoch + targetEpoch := slotNumber / eth2Config.SlotsPerEpoch beaconHead, err := t.bc.GetBeaconHead(t.ctx) if err != nil { return err } finalizedEpoch := beaconHead.FinalizedEpoch - if requiredEpoch > finalizedEpoch { - t.logger.Info("Prices must be reported, waiting until target Epoch is finalized.", slog.Uint64(keys.BlockKey, blockNumber), slog.Uint64(keys.TargetEpochKey, requiredEpoch), slog.Uint64(keys.FinalizedEpochKey, finalizedEpoch)) + if targetEpoch > finalizedEpoch { + t.logger.Info("Prices must be reported, waiting until target Epoch is finalized.", slog.Uint64(keys.BlockKey, targetBlockNumber), slog.Uint64(keys.TargetEpochKey, targetEpoch), slog.Uint64(keys.FinalizedEpochKey, finalizedEpoch)) return nil } @@ -198,17 +153,17 @@ func (t *SubmitRplPrice) Run(state *state.NetworkState) error { t.logger.Info("Starting price report in a separate thread.") // Get RPL price at block - rplPrice, err := t.getRplTwap(blockNumber) + rplPrice, err := t.getRplTwap(targetBlockNumber) if err != nil { t.handleError(err) return } // Log - t.logger.Info("Retrieved RPL price", slog.Uint64(keys.BlockKey, blockNumber), slog.Float64(keys.PriceKey, math.RoundDown(eth.WeiToEth(rplPrice), 6))) + t.logger.Info("Retrieved RPL price", slog.Uint64(keys.BlockKey, targetBlockNumber), slog.Float64(keys.PriceKey, math.RoundDown(eth.WeiToEth(rplPrice), 6))) // Check if we have reported these specific values before - hasSubmittedSpecific, err := t.hasSubmittedSpecificBlockPrices(nodeAddress, blockNumber, uint64(nextSubmissionTime.Unix()), rplPrice, true) + hasSubmittedSpecific, err := t.hasSubmittedSpecificBlockPrices(nodeAddress, targetBlockNumber, uint64(submissionTimestamp), rplPrice, true) if err != nil { t.handleError(err) return @@ -221,20 +176,20 @@ func (t *SubmitRplPrice) Run(state *state.NetworkState) error { } // We haven't submitted these values, check if we've submitted any for this block so we can log it - hasSubmitted, err := t.hasSubmittedBlockPrices(nodeAddress, blockNumber, uint64(nextSubmissionTime.Unix()), true) + hasSubmitted, err := t.hasSubmittedBlockPrices(nodeAddress, targetBlockNumber, uint64(submissionTimestamp), true) if err != nil { t.handleError(err) return } if hasSubmitted { - t.logger.Info("Have previously submitted out-of-date prices, trying again...", slog.Uint64(keys.BlockKey, blockNumber)) + t.logger.Info("Have previously submitted out-of-date prices, trying again...", slog.Uint64(keys.BlockKey, targetBlockNumber)) } // Log t.logger.Info("Submitting RPL price...") // Submit RPL price - if err := t.submitRplPrice(blockNumber, uint64(nextSubmissionTime.Unix()), rplPrice, true); err != nil { + if err := t.submitRplPrice(targetBlockNumber, uint64(submissionTimestamp), rplPrice, true); err != nil { t.handleError(fmt.Errorf("error submitting RPL price: %w", err)) return } diff --git a/rocketpool-daemon/watchtower/utils/utils.go b/rocketpool-daemon/watchtower/utils/utils.go index 66cae504b..2614c8e7a 100644 --- a/rocketpool-daemon/watchtower/utils/utils.go +++ b/rocketpool-daemon/watchtower/utils/utils.go @@ -34,18 +34,37 @@ func GetWatchtowerPrioFee(cfg *config.SmartNodeConfig) float64 { return setting } -func FindLastExistingELBlockFromSlot(ctx context.Context, bc beacon.IBeaconClient, slotNumber uint64) (beacon.Eth1Data, error) { - ecBlock := beacon.Eth1Data{} +func FindLastBlockWithExecutionPayload(ctx context.Context, bc beacon.IBeaconClient, slotNumber uint64) (beacon.BeaconBlock, error) { + beaconBlock := beacon.BeaconBlock{} var err error for blockExists, searchSlot := false, slotNumber; !blockExists; searchSlot -= 1 { - ecBlock, blockExists, err = bc.GetEth1DataForEth2Block(ctx, strconv.FormatUint(searchSlot, 10)) + beaconBlock, blockExists, err = bc.GetBeaconBlock(ctx, strconv.FormatUint(searchSlot, 10)) if err != nil { - return ecBlock, err + return beacon.BeaconBlock{}, err } // If we go back more than 32 slots, error out if slotNumber-searchSlot > 32 { - return ecBlock, fmt.Errorf("could not find EL block from slot %d", slotNumber) + return beacon.BeaconBlock{}, fmt.Errorf("could not find EL block from slot %d", slotNumber) } } - return ecBlock, nil + return beaconBlock, nil +} + +func FindNextSubmissionTimestamp(latestBlockTimestamp int64, referenceTimestamp int64, submissionIntervalInSeconds int64) (int64, error) { + if latestBlockTimestamp == 0 || referenceTimestamp == 0 || submissionIntervalInSeconds == 0 { + return 0, fmt.Errorf("FindNextSubmissionTimestamp can't use zero values") + } + + // Calculate the difference between latestBlockTime and the reference timestamp + timeDifference := latestBlockTimestamp - referenceTimestamp + if timeDifference < 0 { + return 0, fmt.Errorf("FindNextSubmissionTimestamp referenceTimestamp in the future") + } + + // Calculate the remainder to find out how far off from a multiple of the interval the current time is + remainder := timeDifference % submissionIntervalInSeconds + + // Subtract the remainder from current time to find the first multiple of the interval in the past + submissionTimeRef := latestBlockTimestamp - remainder + return submissionTimeRef, nil } diff --git a/rocketpool-daemon/watchtower/utils/utils_test.go b/rocketpool-daemon/watchtower/utils/utils_test.go new file mode 100644 index 000000000..f055830e6 --- /dev/null +++ b/rocketpool-daemon/watchtower/utils/utils_test.go @@ -0,0 +1,48 @@ +package utils + +import ( + "fmt" + "testing" + "time" +) + +func TestFindNextSubmission(t *testing.T) { + latestBlockTimestamp := 1713420045 // Same day + referenceTimestamp := 1713420000 // 06:00AM + intervalInSeconds := 86400 + result, err := FindNextSubmissionTimestamp(int64(latestBlockTimestamp), int64(referenceTimestamp), int64(intervalInSeconds)) + if err != nil { + t.Fatal(err) + } + fmt.Printf("%s", time.Unix(result, 0).String()) + if result != 1713420000 { + t.Fatalf("Wrong result") + } + latestBlockTimestamp = 1713510000 + result, err = FindNextSubmissionTimestamp(int64(latestBlockTimestamp), int64(referenceTimestamp), int64(intervalInSeconds)) + if err != nil { + t.Fatal(err) + } + if result != 1713506400 { + t.Fatalf("Wrong result") + } + // Test zero values + _, err = FindNextSubmissionTimestamp(int64(latestBlockTimestamp), int64(referenceTimestamp), 0) + if err == nil { + t.Fatalf("Should have errored after using 0 for the interval") + } + _, err = FindNextSubmissionTimestamp(int64(latestBlockTimestamp), 0, int64(intervalInSeconds)) + if err == nil { + t.Fatalf("Should have errored after using 0 for the reference timestamp") + } + _, err = FindNextSubmissionTimestamp(0, int64(referenceTimestamp), int64(intervalInSeconds)) + if err == nil { + t.Fatalf("Should have errored after using 0 for the latest block timestamp") + } + + // Test reference timestamp in the future + _, err = FindNextSubmissionTimestamp(int64(latestBlockTimestamp), int64(latestBlockTimestamp+86400), int64(intervalInSeconds)) + if err == nil { + t.Fatalf("Should have error when using a reference date in the future") + } +} diff --git a/shared/config/ids/ids.go b/shared/config/ids/ids.go index d85c02426..3a14cbfae 100644 --- a/shared/config/ids/ids.go +++ b/shared/config/ids/ids.go @@ -7,25 +7,26 @@ const ( SmartNodeID string = "smartNode" // Smart Node parameter IDs - NetworkID string = "network" - ClientModeID string = "clientMode" - UserDataPathID string = "rpUserDataDir" - ProjectNameID string = "projectName" - WatchtowerStatePath string = "watchtowerStatePath" - AutoTxMaxFeeID string = "autoTxMaxFee" - MaxPriorityFeeID string = "maxPriorityFee" - AutoTxGasThresholdID string = "autoTxGasThreshold" - DistributeThresholdID string = "distributeThreshold" - RewardsTreeModeID string = "rewardsTreeMode" - RewardsTreeCustomUrlID string = "rewardsTreeCustomUrl" - ArchiveEcUrlID string = "archiveEcUrl" - WatchtowerMaxFeeOverrideID string = "watchtowerMaxFeeOverride" - WatchtowerPriorityFeeOverrideID string = "watchtowerPriorityFeeOverride" - UseRollingRecordsID string = "useRollingRecords" - RecordCheckpointIntervalID string = "recordCheckpointInterval" - CheckpointRetentionLimitID string = "checkpointRetentionLimit" - RecordsPathID string = "recordsPath" - VerifyProposalsID string = "verifyProposals" + NetworkID string = "network" + ClientModeID string = "clientMode" + UserDataPathID string = "rpUserDataDir" + ProjectNameID string = "projectName" + WatchtowerStatePath string = "watchtowerStatePath" + AutoTxMaxFeeID string = "autoTxMaxFee" + MaxPriorityFeeID string = "maxPriorityFee" + AutoTxGasThresholdID string = "autoTxGasThreshold" + DistributeThresholdID string = "distributeThreshold" + RewardsTreeModeID string = "rewardsTreeMode" + PriceBalanceSubmissionReferenceTimestampID string = "priceBalanceSubmissionReferenceTimestamp" + RewardsTreeCustomUrlID string = "rewardsTreeCustomUrl" + ArchiveEcUrlID string = "archiveEcUrl" + WatchtowerMaxFeeOverrideID string = "watchtowerMaxFeeOverride" + WatchtowerPriorityFeeOverrideID string = "watchtowerPriorityFeeOverride" + UseRollingRecordsID string = "useRollingRecords" + RecordCheckpointIntervalID string = "recordCheckpointInterval" + CheckpointRetentionLimitID string = "checkpointRetentionLimit" + RecordsPathID string = "recordsPath" + VerifyProposalsID string = "verifyProposals" // Subconfig IDs LoggingID string = "logging" diff --git a/shared/config/migration/v1.go b/shared/config/migration/v1.go index 233f6c2d2..9ef360c16 100644 --- a/shared/config/migration/v1.go +++ b/shared/config/migration/v1.go @@ -68,6 +68,7 @@ func upgradeFromV1(oldConfig map[string]any) (map[string]any, error) { newSmartnodeConfig[ids.AutoTxGasThresholdID] = legacySmartnodeConfig["minipoolStakeGasThreshold"] newSmartnodeConfig[ids.DistributeThresholdID] = legacySmartnodeConfig["distributeThreshold"] newSmartnodeConfig[ids.RewardsTreeModeID] = legacySmartnodeConfig["rewardsTreeMode"] + newSmartnodeConfig[ids.PriceBalanceSubmissionReferenceTimestampID] = legacySmartnodeConfig["priceBalanceSubmissionReferenceTimestamp"] newSmartnodeConfig[ids.RewardsTreeCustomUrlID] = legacySmartnodeConfig["rewardsTreeCustomUrl"] newSmartnodeConfig[ids.WatchtowerMaxFeeOverrideID] = legacySmartnodeConfig["watchtowerMaxFeeOverride"] newSmartnodeConfig[ids.WatchtowerPriorityFeeOverrideID] = legacySmartnodeConfig["watchtowerPrioFeeOverride"] diff --git a/shared/config/resources.go b/shared/config/resources.go index 38f4da5f0..5e20e7b16 100644 --- a/shared/config/resources.go +++ b/shared/config/resources.go @@ -147,25 +147,27 @@ func newRocketPoolResources(network config.Network) *RocketPoolResources { // Holesky holeskyResources := &RocketPoolResources{ - NetworkResources: config.NewResources(config.Network_Holesky), - StakeUrl: "https://testnet.rocketpool.net", - StorageAddress: common.HexToAddress("0x594Fb75D3dc2DFa0150Ad03F99F97817747dd4E1"), - RethAddress: common.HexToAddress("0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1"), - RplTokenAddress: common.HexToAddress("0x1Cc9cF5586522c6F483E84A19c3C2B0B6d027bF0"), - V1_0_0_RewardsPoolAddress: nil, - V1_0_0_ClaimNodeAddress: nil, - V1_0_0_ClaimTrustedNodeAddress: nil, - V1_0_0_MinipoolManagerAddress: nil, - V1_1_0_NetworkPricesAddress: nil, - V1_1_0_NodeStakingAddress: nil, - V1_1_0_NodeDepositAddress: nil, - V1_1_0_MinipoolQueueAddress: nil, - V1_1_0_MinipoolFactoryAddress: nil, - V1_2_0_NetworkPricesAddress: hexToAddressPtr("0x029d946F28F93399a5b0D09c879FC8c94E596AEb"), - V1_2_0_NetworkBalancesAddress: hexToAddressPtr("0x9294Fc6F03c64Cc217f5BE8697EA3Ed2De77e2F8"), - SnapshotDelegationAddress: nil, - SnapshotApiDomain: "", - PreviousRewardsPoolAddresses: []common.Address{}, + NetworkResources: config.NewResources(config.Network_Holesky), + StakeUrl: "https://testnet.rocketpool.net", + StorageAddress: common.HexToAddress("0x594Fb75D3dc2DFa0150Ad03F99F97817747dd4E1"), + RethAddress: common.HexToAddress("0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1"), + RplTokenAddress: common.HexToAddress("0x1Cc9cF5586522c6F483E84A19c3C2B0B6d027bF0"), + V1_0_0_RewardsPoolAddress: nil, + V1_0_0_ClaimNodeAddress: nil, + V1_0_0_ClaimTrustedNodeAddress: nil, + V1_0_0_MinipoolManagerAddress: nil, + V1_1_0_NetworkPricesAddress: nil, + V1_1_0_NodeStakingAddress: nil, + V1_1_0_NodeDepositAddress: nil, + V1_1_0_MinipoolQueueAddress: nil, + V1_1_0_MinipoolFactoryAddress: nil, + V1_2_0_NetworkPricesAddress: hexToAddressPtr("0x029d946F28F93399a5b0D09c879FC8c94E596AEb"), + V1_2_0_NetworkBalancesAddress: hexToAddressPtr("0x9294Fc6F03c64Cc217f5BE8697EA3Ed2De77e2F8"), + SnapshotDelegationAddress: nil, + SnapshotApiDomain: "", + PreviousRewardsPoolAddresses: []common.Address{ + common.HexToAddress("0x4a625C617a44E60F74E3fe3bf6d6333b63766e91"), + }, PreviousProtocolDaoVerifierAddresses: []common.Address{}, PreviousRocketNetworkPricesAddresses: []common.Address{ common.HexToAddress("0x029d946f28f93399a5b0d09c879fc8c94e596aeb"), diff --git a/shared/config/settings.go b/shared/config/settings.go index d42481e10..e2a9d0ff2 100644 --- a/shared/config/settings.go +++ b/shared/config/settings.go @@ -4,8 +4,9 @@ import "time" const ( // Watchtower - WatchtowerMaxFeeDefault uint64 = 200 - WatchtowerPriorityFeeDefault uint64 = 3 + WatchtowerMaxFeeDefault uint64 = 200 + WatchtowerPriorityFeeDefault uint64 = 3 + PBSubmission_6AM PBSubmissionRef = 1713420000 // Daemon EventLogInterval int = 1000 diff --git a/shared/config/smartnode-config.go b/shared/config/smartnode-config.go index ac3248c00..6f482145c 100644 --- a/shared/config/smartnode-config.go +++ b/shared/config/smartnode-config.go @@ -28,25 +28,26 @@ const ( // The master configuration struct type SmartNodeConfig struct { // Smart Node settings - Network config.Parameter[config.Network] - ClientMode config.Parameter[config.ClientMode] - ProjectName config.Parameter[string] - UserDataPath config.Parameter[string] - WatchtowerStatePath config.Parameter[string] - AutoTxMaxFee config.Parameter[float64] - MaxPriorityFee config.Parameter[float64] - AutoTxGasThreshold config.Parameter[float64] - DistributeThreshold config.Parameter[float64] - RewardsTreeMode config.Parameter[RewardsMode] - RewardsTreeCustomUrl config.Parameter[string] - ArchiveEcUrl config.Parameter[string] - WatchtowerMaxFeeOverride config.Parameter[float64] - WatchtowerPriorityFeeOverride config.Parameter[float64] - UseRollingRecords config.Parameter[bool] - RecordCheckpointInterval config.Parameter[uint64] - CheckpointRetentionLimit config.Parameter[uint64] - RecordsPath config.Parameter[string] - VerifyProposals config.Parameter[bool] + Network config.Parameter[config.Network] + ClientMode config.Parameter[config.ClientMode] + ProjectName config.Parameter[string] + UserDataPath config.Parameter[string] + WatchtowerStatePath config.Parameter[string] + AutoTxMaxFee config.Parameter[float64] + MaxPriorityFee config.Parameter[float64] + AutoTxGasThreshold config.Parameter[float64] + DistributeThreshold config.Parameter[float64] + RewardsTreeMode config.Parameter[RewardsMode] + RewardsTreeCustomUrl config.Parameter[string] + PriceBalanceSubmissionReferenceTimestamp config.Parameter[int64] + ArchiveEcUrl config.Parameter[string] + WatchtowerMaxFeeOverride config.Parameter[float64] + WatchtowerPriorityFeeOverride config.Parameter[float64] + UseRollingRecords config.Parameter[bool] + RecordCheckpointInterval config.Parameter[uint64] + CheckpointRetentionLimit config.Parameter[uint64] + RecordsPath config.Parameter[string] + VerifyProposals config.Parameter[bool] // Logging Logging *config.LoggerConfig @@ -291,6 +292,20 @@ func NewSmartNodeConfig(rpDir string, isNativeMode bool) *SmartNodeConfig { }, }, + PriceBalanceSubmissionReferenceTimestamp: config.Parameter[int64]{ + ParameterCommon: &config.ParameterCommon{ + ID: ids.PriceBalanceSubmissionReferenceTimestampID, + Name: "P/B Submission Time Ref", + Description: "Prices and balances submission time reference. An Unix timestamp used by oDAO members as an initial reference to calculate when submissions are due based on the onchain stored submission interval value.", + AffectsContainers: []config.ContainerID{config.ContainerID_Daemon}, + CanBeBlank: false, + OverwriteOnUpgrade: true, + }, + Default: map[config.Network]int64{ + config.Network_All: int64(PBSubmission_6AM), + }, + }, + RewardsTreeCustomUrl: config.Parameter[string]{ ParameterCommon: &config.ParameterCommon{ ID: ids.RewardsTreeCustomUrlID, @@ -456,6 +471,7 @@ func (cfg *SmartNodeConfig) GetParameters() []config.IParameter { &cfg.AutoTxGasThreshold, &cfg.DistributeThreshold, &cfg.RewardsTreeMode, + &cfg.PriceBalanceSubmissionReferenceTimestamp, &cfg.RewardsTreeCustomUrl, &cfg.WatchtowerMaxFeeOverride, &cfg.WatchtowerPriorityFeeOverride, diff --git a/shared/config/types.go b/shared/config/types.go index 89f0685d7..4a8d0160f 100644 --- a/shared/config/types.go +++ b/shared/config/types.go @@ -2,6 +2,8 @@ package config import "github.com/rocket-pool/node-manager-core/config" +type PBSubmissionRef int + // A MEV relay type MevRelay struct { ID MevRelayID diff --git a/shared/keys/keys.go b/shared/keys/keys.go index 345494abb..cccaec3ac 100644 --- a/shared/keys/keys.go +++ b/shared/keys/keys.go @@ -77,5 +77,6 @@ const ( SubmittedKey string = "submitted" NextKey string = "next" TimeKey string = "time" + RequiredEpochKey string = "requiredEpoch" ModuleKey string = "module" ) diff --git a/shared/types/api/pdao.go b/shared/types/api/pdao.go index 4cebffe1d..e70446c5a 100644 --- a/shared/types/api/pdao.go +++ b/shared/types/api/pdao.go @@ -165,52 +165,57 @@ type ProtocolDaoRewardsPercentagesData struct { } type ProtocolDaoProposeSettingData struct { - CanPropose bool `json:"canPropose"` - UnknownSetting bool `json:"unknownSetting"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + UnknownSetting bool `json:"unknownSetting"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoGeneralProposeData struct { - CanPropose bool `json:"canPropose"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoProposeInviteToSecurityCouncilData struct { - CanPropose bool `json:"canPropose"` - MemberAlreadyExists bool `json:"memberAlreadyExists"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + MemberAlreadyExists bool `json:"memberAlreadyExists"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoProposeKickFromSecurityCouncilData struct { - CanPropose bool `json:"canPropose"` - MemberDoesNotExist bool `json:"memberDoesNotExist"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + MemberDoesNotExist bool `json:"memberDoesNotExist"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoProposeKickMultiFromSecurityCouncilData struct { - CanPropose bool `json:"canPropose"` - NonexistingMembers []common.Address `json:"nonexistingMembers"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + NonexistingMembers []common.Address `json:"nonexistingMembers"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoProposeReplaceMemberOfSecurityCouncilData struct { @@ -220,6 +225,7 @@ type ProtocolDaoProposeReplaceMemberOfSecurityCouncilData struct { InsufficientRpl bool `json:"insufficientRpl"` StakedRpl *big.Int `json:"stakedRpl"` LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` ProposalBond *big.Int `json:"proposalBond"` TxInfo *eth.TransactionInfo `json:"txInfo"` } @@ -271,13 +277,14 @@ type ProtocolDaoFinalizeProposalData struct { } type ProtocolDaoProposeRecurringSpendUpdateData struct { - CanPropose bool `json:"canPropose"` - DoesNotExist bool `json:"doesNotExist"` - InsufficientRpl bool `json:"insufficientRpl"` - StakedRpl *big.Int `json:"stakedRpl"` - LockedRpl *big.Int `json:"lockedRpl"` - ProposalBond *big.Int `json:"proposalBond"` - TxInfo *eth.TransactionInfo `json:"txInfo"` + CanPropose bool `json:"canPropose"` + DoesNotExist bool `json:"doesNotExist"` + InsufficientRpl bool `json:"insufficientRpl"` + StakedRpl *big.Int `json:"stakedRpl"` + LockedRpl *big.Int `json:"lockedRpl"` + IsRplLockingDisallowed bool `json:"isRplLockingDisallowed"` + ProposalBond *big.Int `json:"proposalBond"` + TxInfo *eth.TransactionInfo `json:"txInfo"` } type ProtocolDaoInitializeVotingData struct { @@ -290,3 +297,7 @@ type ProtocolDaoCurrentVotingDelegateData struct { AccountAddress common.Address `json:"accountAddress"` VotingDelegate common.Address `json:"votingDelegate"` } + +type ProtocolDaoGetVotingPowerData struct { + VotingPower *big.Int `json:"votingPower"` +}