Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pdao status and signalling address commands #599

Merged
merged 29 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1cbc298
Init rocketpool-cli pdao status
thomaspanf Jul 10, 2024
7f264f9
Init rocketpool-daemon pdao status
thomaspanf Jul 10, 2024
05b8a8f
Init RocketSignerRegistry binding
thomaspanf Jul 10, 2024
5c5fc4b
Add RocketSignerRegistry to ServiceProvider
thomaspanf Jul 11, 2024
59cf695
Add registry binding
thomaspanf Jul 11, 2024
f0c1ddc
Populating fields in API response
thomaspanf Jul 12, 2024
9cecead
Init ParseEIP712 helper function
thomaspanf Jul 13, 2024
83b7318
Init signalling address commands
thomaspanf Jul 14, 2024
2095587
Register routes for set/clear signalling address
thomaspanf Jul 15, 2024
4606c10
Add voteCount response
thomaspanf Jul 15, 2024
701c37a
Fixed issue in registry binding
thomaspanf Jul 16, 2024
3f4e01d
Add api route for clear signalling address
thomaspanf Jul 16, 2024
4dddd20
Add cli and api route for set/clear signalling address
thomaspanf Jul 17, 2024
5ff02e2
Remove unused structs
thomaspanf Jul 17, 2024
c03878e
Fix print statements and terminal escape codes
thomaspanf Jul 18, 2024
8a91b72
Add PrintNetwork and error handling for unregistered nodes
thomaspanf Jul 18, 2024
33a089c
Remove else after return and unused fields
thomaspanf Jul 18, 2024
32a7c96
Remove const, fix message and nil pointer
thomaspanf Jul 18, 2024
276d4ef
Add api call to grab active snapshot proposals
thomaspanf Jul 19, 2024
6fce08c
Edit response to improve error output
thomaspanf Jul 19, 2024
fee1e68
Moved eip712 signature validation logic into its own package
thomaspanf Jul 19, 2024
0d690b6
Refactor package eip712 to use Decode and Encode, added unit tests fo…
thomaspanf Jul 22, 2024
05e2934
Update shared/eip712/eip712.go
thomaspanf Jul 22, 2024
cab82dd
Add Validate to eip712 package
thomaspanf Jul 22, 2024
8b4317d
Add test for decoding invalid length signature, fix linter issues
thomaspanf Jul 22, 2024
a6cde79
Refactor Decode/Encode to fulfil TextMarshaler interface
thomaspanf Jul 23, 2024
edb9fc3
Add String method and unit tests for Validate
thomaspanf Jul 23, 2024
5895fce
Fix test case names
thomaspanf Jul 23, 2024
b669d2e
Refactor UnmarshalText and MarshalText
thomaspanf Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions client/pdao.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,20 @@ func (r *PDaoRequester) GetCurrentVotingDelegate() (*types.ApiResponse[api.Proto
}

// Get the pDAO status
func (r *PDaoRequester) GetStatus() (*types.ApiResponse[api.ProtocolDAOStatusResponse], error) {
return client.SendGetRequest[api.ProtocolDAOStatusResponse](r, "get-status", "GetStatus", nil)
func (r *PDaoRequester) GetStatus() (*types.ApiResponse[api.ProtocolDaoStatusResponse], error) {
return client.SendGetRequest[api.ProtocolDaoStatusResponse](r, "get-status", "GetStatus", nil)
}

// Set the signalling address for the node
func (r *PDaoRequester) SetSignallingAddress(signallingAddress common.Address, signature string) (*types.ApiResponse[types.TxInfoData], error) {
args := map[string]string{
"signallingAddress": signallingAddress.Hex(),
"signature": string(signature),
}
return client.SendGetRequest[types.TxInfoData](r, "set-signalling-address", "SetSignallingAddress", args)
}

// Set the signalling address for the node
func (r *PDaoRequester) ClearSignallingAddress() (*types.ApiResponse[types.TxInfoData], error) {
return client.SendGetRequest[types.TxInfoData](r, "clear-signalling-address", "ClearSignallingAddress", nil)
}
92 changes: 65 additions & 27 deletions rocketpool-cli/commands/pdao/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,71 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
},
},

{
Name: "initialize-voting",
Aliases: []string{"iv"},
Usage: "Unlocks a node operator's voting power (only required for node operators who registered before governance structure was in place)",
Action: func(c *cli.Context) error {
// Run
return initializeVoting(c)
},
},

{
Name: "set-signalling-address",
Aliases: []string{"ssa"},
Usage: "Set the address you want to use to represent your node on Snapshot",
ArgsUsage: "signalling-address signature",
Flags: []cli.Flag{
cliutils.YesFlag,
},
Action: func(c *cli.Context) error {
// Validate args
utils.ValidateArgCount(c, 2)

signallingAddress, err := input.ValidateAddress("signalling-address", c.Args().Get(0))
if err != nil {
return err
}
// leaving this out until https://github.com/rocket-pool/node-manager-core/pull/17 is merged
// signature, err := input.ValidateSignature("signature", c.Args().Get(1))
// if err != nil {
// return err
// }
signature := c.Args().Get(1)

// Run
return setSignallingAddress(c, signallingAddress, signature)
},
},

{
Name: "clear-signalling-address",
Aliases: []string{"csa"},
Usage: "Clear the node's signalling address",
Action: func(c *cli.Context) error {
// Run
return clearSignallingAddress(c)
},
},

{
Name: "set-voting-delegate",
Aliases: []string{"svd"},
Usage: "Set the address you want to use when voting on Rocket Pool on-chain governance proposals, or the address you want to delegate your voting power to.",
ArgsUsage: "address",
Flags: []cli.Flag{
cliutils.YesFlag,
},
Action: func(c *cli.Context) error {
// Validate args
utils.ValidateArgCount(c, 1)
delegate := c.Args().Get(0)
// Run
return setVotingDelegate(c, delegate)
},
},

{
Name: "claim-bonds",
Aliases: []string{"cb"},
Expand Down Expand Up @@ -469,33 +534,6 @@ func RegisterCommands(app *cli.App, name string, aliases []string) {
},
},
},

{
Name: "initialize-voting",
Aliases: []string{"iv"},
Usage: "Unlocks a node operator's voting power (only required for node operators who registered before governance structure was in place)",
Action: func(c *cli.Context) error {
// Run
return initializeVoting(c)
},
},

{
Name: "set-voting-delegate",
Aliases: []string{"svd"},
Usage: "Set the address you want to use when voting on Rocket Pool on-chain governance proposals, or the address you want to delegate your voting power to.",
ArgsUsage: "address",
Flags: []cli.Flag{
cliutils.YesFlag,
},
Action: func(c *cli.Context) error {
// Validate args
utils.ValidateArgCount(c, 1)
delegate := c.Args().Get(0)
// Run
return setVotingDelegate(c, delegate)
},
},
},
})
}
70 changes: 70 additions & 0 deletions rocketpool-cli/commands/pdao/signalling-address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package pdao

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/rocket-pool/smartnode/v2/rocketpool-cli/client"
"github.com/rocket-pool/smartnode/v2/rocketpool-cli/utils/tx"
"github.com/urfave/cli/v2"
)

func setSignallingAddress(c *cli.Context, signallingAddress common.Address, signature string) error {
// Get RP client
rp, err := client.NewClientFromCtx(c)
if err != nil {
return err
}

// Build the TX
response, err := rp.Api.PDao.SetSignallingAddress(signallingAddress, signature)
if err != nil {
return fmt.Errorf("Error setting the signalling address: %w", err)
}

validated, err := tx.HandleTx(c, rp, response.Data.TxInfo,
"Are you sure you want to set your signalling address?",
"setting signalling address",
"Setting signalling address...",
)
if err != nil {
return err
}
if !validated {
return nil
}

return nil

}

func clearSignallingAddress(c *cli.Context) error {

// Get RP client
rp, err := client.NewClientFromCtx(c)
if err != nil {
return err
}

// Build the TX
response, err := rp.Api.PDao.ClearSignallingAddress()
if err != nil {
return fmt.Errorf("Error clearing the signalling address: %w", err)
}

validated, err := tx.HandleTx(c, rp, response.Data.TxInfo,
"Are you sure you want to clear the current signalling address?",
"clearing signalling address",
"Clearing signalling address...",
)
if err != nil {
return err
}
if !validated {
return nil
}

// Log & return
fmt.Println("The node's signalling address has been sucessfully cleared.")
return nil
}
97 changes: 62 additions & 35 deletions rocketpool-cli/commands/pdao/status.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pdao

import (
"errors"
"fmt"
"math/big"

Expand All @@ -12,13 +13,16 @@ import (
"github.com/rocket-pool/rocketpool-go/v2/types"
"github.com/rocket-pool/smartnode/v2/rocketpool-cli/client"
"github.com/rocket-pool/smartnode/v2/rocketpool-cli/utils"
"github.com/rocket-pool/smartnode/v2/rocketpool-cli/utils/terminal"
"github.com/rocket-pool/smartnode/v2/shared/types/api"
)

const (
colorBlue string = "\033[36m"
colorReset string = "\033[0m"
colorGreen string = "\033[32m"
colorBlue string = terminal.ColorBlue
colorReset string = terminal.ColorReset
colorGreen string = terminal.ColorGreen

thomaspanf marked this conversation as resolved.
Show resolved Hide resolved
signallingAddressLink string = "https://docs.rocketpool.net/guides/houston/participate#setting-your-snapshot-signalling-address"
thomaspanf marked this conversation as resolved.
Show resolved Hide resolved
)

func getStatus(c *cli.Context) error {
Expand All @@ -29,6 +33,29 @@ func getStatus(c *cli.Context) error {
return err
}

// Get the config
cfg, isNew, err := rp.LoadConfig()
if err != nil {
return fmt.Errorf("error loading configuration: %w", err)
}

// Get wallet status
statusResponse, err := rp.Api.Wallet.Status()
if err != nil {
return err
}
walletStatus := statusResponse.Data.WalletStatus

// Print what network we're on
err = utils.PrintNetwork(cfg.Network.Value, isNew)
if err != nil {
return err
}
// rp.rp.Api.PDao.GetStatus() will fail with an error, but we can short-circuit it here.
if !walletStatus.Address.HasAddress {
return errors.New("No node address is loaded.")
thomaspanf marked this conversation as resolved.
Show resolved Hide resolved
}

// Get PDAO status at the latest block
response, err := rp.Api.PDao.GetStatus()
if err != nil {
Expand All @@ -42,42 +69,43 @@ func getStatus(c *cli.Context) error {
}

// Get protocol DAO proposals
claimableBondsResponse, err := rp.Api.PDao.GetClaimableBonds()
if err != nil {
return fmt.Errorf("error checking for claimable bonds: %w", err)
var claimableBonds []api.BondClaimResult
if response.Data.IsNodeRegistered {
claimableBondsResponse, err := rp.Api.PDao.GetClaimableBonds()
if err != nil {
return fmt.Errorf("error checking for claimable bonds: %w", err)
}
claimableBonds = claimableBondsResponse.Data.ClaimableBonds
}
claimableBonds := claimableBondsResponse.Data.ClaimableBonds

// Snapshot voting status
fmt.Printf("%s=== Snapshot Voting ===%s\n", colorGreen, colorReset)
// Signalling Status
fmt.Printf("%s=== Signalling on Snapshot ===%s\n", colorGreen, colorReset)
blankAddress := common.Address{}
if response.Data.SnapshotVotingDelegate == blankAddress {
fmt.Println("The node does not currently have a voting delegate set, which means it can only vote directly on Snapshot proposals (using a hardware wallet with the node mnemonic loaded).\nRun `rocketpool n sv <address>` to vote from a different wallet or have a delegate represent you. (See https://delegates.rocketpool.net for options)")
if response.Data.SignallingAddress == blankAddress {
fmt.Println("The node does not currently have a snapshot signalling address set.")
fmt.Printf("To learn more about snapshot signalling, please visit %s.\n", signallingAddressLink)
} else {
fmt.Printf("The node has a voting delegate of %s%s%s which can represent it when voting on Rocket Pool Snapshot governance proposals.\n", colorBlue, response.Data.SnapshotVotingDelegateFormatted, colorReset)
fmt.Printf("The node has a signalling address of %s%s%s which can represent it when voting on Rocket Pool Snapshot governance proposals.\n", colorBlue, response.Data.SignallingAddressFormatted, colorReset)
}

if response.Data.SnapshotResponse.Error != "" {
fmt.Printf("Unable to fetch latest voting information from snapshot.org: %s\n", response.Data.SnapshotResponse.Error)
} else {
voteCount := 0
voteCount := response.Data.SnapshotResponse.VoteCount()
if len(response.Data.SnapshotResponse.ActiveSnapshotProposals) == 0 {
fmt.Print("Rocket Pool has no Snapshot governance proposals being voted on.\n")
fmt.Println("Rocket Pool has no Snapshot governance proposals being voted on.")
} else {
fmt.Printf("Rocket Pool has %d Snapshot governance proposal(s) being voted on. You have voted on %d of those. See details using 'rocketpool network dao-proposals'.\n", len(response.Data.SnapshotResponse.ActiveSnapshotProposals), voteCount)
}
fmt.Println("")
}
fmt.Println()

// Onchain Voting Status
fmt.Printf("%s=== Onchain Voting ===%s\n", colorGreen, colorReset)
if response.Data.IsVotingInitialized {
fmt.Println("The node has been initialized for onchain voting.")

fmt.Printf("The node %s%s%s has been initialized for onchain voting.\n", colorBlue, response.Data.AccountAddressFormatted, colorReset)
} else {
fmt.Println("The node has NOT been initialized for onchain voting. You need to run `rocketpool pdao initialize-voting` to participate in onchain votes.")
fmt.Printf("The node %s%s%s has NOT been initialized for onchain voting. You need to run `rocketpool pdao initialize-voting` to participate in onchain votes.\n", colorBlue, response.Data.AccountAddressFormatted, colorReset)
}

if response.Data.OnchainVotingDelegate == blankAddress {
fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals after it initializes voting.")
} else if response.Data.OnchainVotingDelegate == response.Data.AccountAddress {
Expand All @@ -86,31 +114,30 @@ func getStatus(c *cli.Context) error {
fmt.Printf("The node has a voting delegate of %s%s%s which can represent it when voting on Rocket Pool onchain governance proposals.\n", colorBlue, response.Data.OnchainVotingDelegateFormatted, colorReset)
}
fmt.Printf("The node's local voting power: %.10f\n", eth.WeiToEth(response.Data.VotingPower))

fmt.Printf("Total voting power delegated to the node: %.10f\n", eth.WeiToEth(response.Data.TotalDelegatedVp))

if response.Data.IsNodeRegistered {
fmt.Printf("Total voting power delegated to the node: %.10f\n", eth.WeiToEth(response.Data.TotalDelegatedVp))
} else {
fmt.Println("The node must register using 'rocketpool node register' to be eligible to receive delegated voting power")
}
fmt.Printf("Network total initialized voting power: %.10f\n", eth.WeiToEth(response.Data.SumVotingPower))

fmt.Println("")
fmt.Println()

// Claimable Bonds Status:
fmt.Printf("%s=== Claimable RPL Bonds ===%s\n", colorGreen, colorReset)
if response.Data.IsRPLLockingAllowed {
fmt.Print("The node is allowed to lock RPL to create governance proposals/challenges.\n")
fmt.Println("The node is allowed to lock RPL to create governance proposals/challenges.")
if response.Data.NodeRPLLocked.Cmp(big.NewInt(0)) != 0 {
fmt.Printf("The node currently has %.6f RPL locked.\n",
utilsMath.RoundDown(eth.WeiToEth(response.Data.NodeRPLLocked), 6))
fmt.Printf("The node currently has %.6f RPL locked.\n", utilsMath.RoundDown(eth.WeiToEth(response.Data.NodeRPLLocked), 6))
}

} else {
fmt.Print("The node is NOT allowed to lock RPL to create governance proposals/challenges. Use 'rocketpool node allow-rpl-locking, to allow RPL locking.\n")
fmt.Println("The node is NOT allowed to lock RPL to create governance proposals/challenges. Use 'rocketpool node allow-rpl-locking` to allow RPL locking.")
}
if len(claimableBonds) == 0 {
fmt.Println("You do not have any unlockable bonds or claimable rewards.")
fmt.Println("The node does not have any unlockable bonds or claimable rewards.")
} else {
fmt.Println("The node has unlockable bonds or claimable rewards available. Use 'rocketpool pdao claim-bonds' to view and claim.")
}
fmt.Println("")
fmt.Println()

// Check if PDAO proposal checking duty is enabled
fmt.Printf("%s=== PDAO Proposal Checking Duty ===%s\n", colorGreen, colorReset)
Expand All @@ -120,7 +147,7 @@ func getStatus(c *cli.Context) error {
} else {
fmt.Println("The node does not have PDAO proposal checking duties enabled (See https://docs.rocketpool.net/guides/houston/pdao#challenge-process to learn more about this duty).")
}
fmt.Println("")
fmt.Println()

// Claimable Bonds Status:
fmt.Printf("%s=== Pending, Active and Succeeded Proposals ===%s\n", colorGreen, colorReset)
Expand Down Expand Up @@ -155,12 +182,12 @@ func getStatus(c *cli.Context) error {
// Print message for Succeeded Proposals
if stateName == "Succeeded" {
succeededExists = true
fmt.Printf("%sThe following proposal(s) have succeeded and are waiting to be executed. Use `rocketpool pdao proposals execute` to execute.%s\n\n", colorBlue, colorReset)
fmt.Printf("%sThe following proposal(s) have succeeded and are waiting to be executed. Use `rocketpool pdao proposals execute` to execute.%s\n", colorBlue, colorReset)
}

// Proposal state count
fmt.Printf("%d %s proposal(s):\n", len(proposals), stateName)
fmt.Println("")
fmt.Println()

// Proposals
for _, proposal := range proposals {
Expand Down
Loading
Loading