summaryrefslogtreecommitdiff
path: root/cmd/goal/account.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/goal/account.go')
-rw-r--r--cmd/goal/account.go175
1 files changed, 129 insertions, 46 deletions
diff --git a/cmd/goal/account.go b/cmd/goal/account.go
index d1bf2f6cf..7d20af38f 100644
--- a/cmd/goal/account.go
+++ b/cmd/goal/account.go
@@ -59,6 +59,7 @@ var (
partKeyOutDir string
partKeyFile string
partKeyDeleteInput bool
+ listpartkeyCompat bool
importDefault bool
mnemonic string
dumpOutFile string
@@ -165,6 +166,9 @@ func init() {
installParticipationKeyCmd.MarkFlagRequired("partkey")
installParticipationKeyCmd.Flags().BoolVar(&partKeyDeleteInput, "delete-input", false, "Acknowledge that installpartkey will delete the input key file")
+ // listpartkey flags
+ listParticipationKeysCmd.Flags().BoolVarP(&listpartkeyCompat, "compatibility", "c", false, "Print output in compatibility mode. This option will be removed in a future release, please use REST API for tooling.")
+
// import flags
importCmd.Flags().BoolVarP(&importDefault, "default", "f", false, "Set this account as the default one")
importCmd.Flags().StringVarP(&mnemonic, "mnemonic", "m", "", "Mnemonic to import (will prompt otherwise)")
@@ -933,7 +937,7 @@ var renewParticipationKeyCmd = &cobra.Command{
}
// Make sure we don't already have a partkey valid for (or after) specified roundLastValid
- parts, err := client.ListParticipationKeys()
+ parts, err := client.ListParticipationKeyFiles()
if err != nil {
reportErrorf(errorRequestFail, err)
}
@@ -991,7 +995,7 @@ func renewPartKeysInDir(dataDir string, lastValidRound uint64, fee uint64, lease
client := ensureAlgodClient(dataDir)
// Build list of accounts to renew from all accounts with part keys present
- parts, err := client.ListParticipationKeys()
+ parts, err := client.ListParticipationKeyFiles()
if err != nil {
return fmt.Errorf(errorRequestFail, err)
}
@@ -1051,12 +1055,73 @@ func renewPartKeysInDir(dataDir string, lastValidRound uint64, fee uint64, lease
return nil
}
+func maxRound(current uint64, next *uint64) uint64 {
+ if next != nil && *next > current {
+ return *next
+ }
+ return current
+}
+
+func uintToStr(number uint64) string {
+ return fmt.Sprintf("%d", number)
+}
+
+// legacyListParticipationKeysCommand prints key information in the same
+// format as earlier versions of goal. Some users are using this information
+// in scripts and need some extra time to migrate to the REST API.
+func legacyListParticipationKeysCommand() {
+ dataDir := ensureSingleDataDir()
+
+ client := ensureGoalClient(dataDir, libgoal.DynamicClient)
+ parts, err := client.ListParticipationKeyFiles()
+ if err != nil {
+ reportErrorf(errorRequestFail, err)
+ }
+
+ var filenames []string
+ for fn := range parts {
+ filenames = append(filenames, fn)
+ }
+ sort.Strings(filenames)
+
+ rowFormat := "%-10s\t%-80s\t%-60s\t%12s\t%12s\t%12s\n"
+ fmt.Printf(rowFormat, "Registered", "Filename", "Parent address", "First round", "Last round", "First key")
+ for _, fn := range filenames {
+ onlineInfoStr := "unknown"
+ onlineAccountInfo, err := client.AccountInformation(parts[fn].Address().GetUserAddress())
+ if err == nil {
+ votingBytes := parts[fn].Voting.OneTimeSignatureVerifier
+ vrfBytes := parts[fn].VRF.PK
+ if onlineAccountInfo.Participation != nil &&
+ (string(onlineAccountInfo.Participation.ParticipationPK) == string(votingBytes[:])) &&
+ (string(onlineAccountInfo.Participation.VRFPK) == string(vrfBytes[:])) &&
+ (onlineAccountInfo.Participation.VoteFirst == uint64(parts[fn].FirstValid)) &&
+ (onlineAccountInfo.Participation.VoteLast == uint64(parts[fn].LastValid)) &&
+ (onlineAccountInfo.Participation.VoteKeyDilution == parts[fn].KeyDilution) {
+ onlineInfoStr = "yes"
+ } else {
+ onlineInfoStr = "no"
+ }
+ }
+ // it's okay to proceed without algod info
+ first, last := parts[fn].ValidInterval()
+ fmt.Printf(rowFormat, onlineInfoStr, fn, parts[fn].Address().GetUserAddress(),
+ fmt.Sprintf("%d", first),
+ fmt.Sprintf("%d", last),
+ fmt.Sprintf("%d.%d", parts[fn].Voting.FirstBatch, parts[fn].Voting.FirstOffset))
+ }
+}
+
var listParticipationKeysCmd = &cobra.Command{
Use: "listpartkeys",
- Short: "List participation keys",
- Long: `List all participation keys tracked by algod, with additional information such as key validity period.`,
+ Short: "List participation keys summary",
+ Long: `List all participation keys tracked by algod along with summary of additional information. For detailed key information use 'partkeyinfo'.`,
Args: validateNoPosArgsFn,
Run: func(cmd *cobra.Command, args []string) {
+ if listpartkeyCompat {
+ legacyListParticipationKeysCommand()
+ return
+ }
dataDir := ensureSingleDataDir()
client := ensureGoalClient(dataDir, libgoal.DynamicClient)
@@ -1065,37 +1130,53 @@ var listParticipationKeysCmd = &cobra.Command{
reportErrorf(errorRequestFail, err)
}
- var filenames []string
- for fn := range parts {
- filenames = append(filenames, fn)
- }
- sort.Strings(filenames)
-
- rowFormat := "%-10s\t%-80s\t%-60s\t%12s\t%12s\t%12s\n"
- fmt.Printf(rowFormat, "Registered", "Filename", "Parent address", "First round", "Last round", "First key")
- for _, fn := range filenames {
+ // Squeezed this into 77 characters.
+ rowFormat := "%-10s %-11s %-15s %10s %11s %10s\n"
+ fmt.Printf(rowFormat, "Registered", "Account", "ParticipationID", "Last Used", "First round", "Last round")
+ for _, part := range parts {
onlineInfoStr := "unknown"
- onlineAccountInfo, err := client.AccountInformation(parts[fn].Address().GetUserAddress())
+ onlineAccountInfo, err := client.AccountInformation(part.Address)
if err == nil {
- votingBytes := parts[fn].Voting.OneTimeSignatureVerifier
- vrfBytes := parts[fn].VRF.PK
+ votingBytes := part.Key.VoteParticipationKey
+ vrfBytes := part.Key.SelectionParticipationKey
if onlineAccountInfo.Participation != nil &&
(string(onlineAccountInfo.Participation.ParticipationPK) == string(votingBytes[:])) &&
(string(onlineAccountInfo.Participation.VRFPK) == string(vrfBytes[:])) &&
- (onlineAccountInfo.Participation.VoteFirst == uint64(parts[fn].FirstValid)) &&
- (onlineAccountInfo.Participation.VoteLast == uint64(parts[fn].LastValid)) &&
- (onlineAccountInfo.Participation.VoteKeyDilution == parts[fn].KeyDilution) {
+ (onlineAccountInfo.Participation.VoteFirst == part.Key.VoteFirstValid) &&
+ (onlineAccountInfo.Participation.VoteLast == part.Key.VoteLastValid) &&
+ (onlineAccountInfo.Participation.VoteKeyDilution == part.Key.VoteKeyDilution) {
onlineInfoStr = "yes"
} else {
onlineInfoStr = "no"
}
+
+ /*
+ // PKI TODO: We could avoid querying the account with something like this.
+ // One problem is that it doesn't account for multiple keys on the same
+ // account, so we'd still need to query the round.
+ if part.EffectiveFirstValid != nil && part.EffectiveLastValid < currentRound {
+ onlineInfoStr = "yes"
+ } else {
+ onlineInfoStr = "no"
+ }
+ */
+
+ // it's okay to proceed without algod info
+ lastUsed := maxRound(0, part.LastVote)
+ lastUsed = maxRound(lastUsed, part.LastBlockProposal)
+ lastUsed = maxRound(lastUsed, part.LastStateProof)
+ lastUsedString := "N/A"
+ if lastUsed != 0 {
+ lastUsedString = uintToStr(lastUsed)
+ }
+ fmt.Printf(rowFormat,
+ onlineInfoStr,
+ fmt.Sprintf("%s...%s", part.Address[:4], part.Address[len(part.Address)-4:]),
+ fmt.Sprintf("%s...", part.Id[:8]),
+ lastUsedString,
+ uintToStr(part.Key.VoteFirstValid),
+ uintToStr(part.Key.VoteLastValid))
}
- // it's okay to proceed without algod info
- first, last := parts[fn].ValidInterval()
- fmt.Printf(rowFormat, onlineInfoStr, fn, parts[fn].Address().GetUserAddress(),
- fmt.Sprintf("%d", first),
- fmt.Sprintf("%d", last),
- fmt.Sprintf("%d.%d", parts[fn].Voting.FirstBatch, parts[fn].Voting.FirstOffset))
}
},
}
@@ -1276,14 +1357,11 @@ var importRootKeysCmd = &cobra.Command{
},
}
-type partkeyInfo struct {
- _struct struct{} `codec:",omitempty,omitemptyarray"`
- Address string `codec:"acct"`
- FirstValid basics.Round `codec:"first"`
- LastValid basics.Round `codec:"last"`
- VoteID crypto.OneTimeSignatureVerifier `codec:"vote"`
- SelectionID crypto.VRFVerifier `codec:"sel"`
- VoteKeyDilution uint64 `codec:"voteKD"`
+func strOrNA(value *uint64) string {
+ if value == nil {
+ return "N/A"
+ }
+ return uintToStr(*value)
}
var partkeyInfoCmd = &cobra.Command{
@@ -1295,7 +1373,7 @@ var partkeyInfoCmd = &cobra.Command{
onDataDirs(func(dataDir string) {
fmt.Printf("Dumping participation key info from %s...\n", dataDir)
- client := ensureGoalClient(dataDir, libgoal.DynamicClient)
+ client := ensureAlgodClient(dataDir)
// Make sure we don't already have a partkey valid for (or after) specified roundLastValid
parts, err := client.ListParticipationKeys()
@@ -1303,18 +1381,23 @@ var partkeyInfoCmd = &cobra.Command{
reportErrorf(errorRequestFail, err)
}
- for filename, part := range parts {
- fmt.Println("------------------------------------------------------------------")
- info := partkeyInfo{
- Address: part.Address().String(),
- FirstValid: part.FirstValid,
- LastValid: part.LastValid,
- VoteID: part.VotingSecrets().OneTimeSignatureVerifier,
- SelectionID: part.VRFSecrets().PK,
- VoteKeyDilution: part.KeyDilution,
- }
- infoString := protocol.EncodeJSON(&info)
- fmt.Printf("File: %s\n%s\n", filename, string(infoString))
+ for _, part := range parts {
+ fmt.Println()
+ fmt.Printf("Participation ID: %s\n", part.Id)
+ fmt.Printf("Parent address: %s\n", part.Address)
+ fmt.Printf("Last vote round: %s\n", strOrNA(part.LastVote))
+ fmt.Printf("Last block proposal round: %s\n", strOrNA(part.LastBlockProposal))
+ // PKI TODO: enable with state proof support.
+ //fmt.Printf("Last state proof round: %s\n", strOrNA(part.LastStateProof))
+ fmt.Printf("Effective first round: %s\n", strOrNA(part.EffectiveFirstValid))
+ fmt.Printf("Effective last round: %s\n", strOrNA(part.EffectiveLastValid))
+ fmt.Printf("First round: %d\n", part.Key.VoteFirstValid)
+ fmt.Printf("Last round: %d\n", part.Key.VoteLastValid)
+ fmt.Printf("Key dilution: %d\n", part.Key.VoteKeyDilution)
+ fmt.Printf("Selection key: %s\n", base64.StdEncoding.EncodeToString(part.Key.SelectionParticipationKey))
+ fmt.Printf("Voting key: %s\n", base64.StdEncoding.EncodeToString(part.Key.VoteParticipationKey))
+ // PKI TODO: enable with state proof support.
+ //fmt.Printf("State proof key: %s\n", base64.StdEncoding.EncodeToString(part.StateProofKey))
}
})
},