From 6d05c81ee2b15a043fa293cb4b95df339cf69348 Mon Sep 17 00:00:00 2001 From: Noam Nelke Date: Thu, 1 Aug 2024 17:26:12 +0300 Subject: [PATCH 1/3] extract address generation logic --- cmd/genesis.go | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/cmd/genesis.go b/cmd/genesis.go index 1b3432b..9a38b3b 100644 --- a/cmd/genesis.go +++ b/cmd/genesis.go @@ -68,26 +68,7 @@ var verifyCmd = &cobra.Command{ cobra.CheckErr(err) amount *= constants.OneSmesh - // calculate keys - vestingArgs := &multisig.SpawnArguments{ - Required: m, - PublicKeys: keys, - } - if int(vestingArgs.Required) > len(vestingArgs.PublicKeys) { - log.Fatalf("requires more signatures (%d) than public keys (%d) in the wallet\n", - vestingArgs.Required, - len(vestingArgs.PublicKeys), - ) - } - vestingAddress := core.ComputePrincipal(vesting.TemplateAddress, vestingArgs) - vaultArgs := &vault.SpawnArguments{ - Owner: vestingAddress, - TotalAmount: amount, - InitialUnlockAmount: amount / 4, - VestingStart: types.LayerID(constants.VestStart), - VestingEnd: types.LayerID(constants.VestEnd), - } - vaultAddress := core.ComputePrincipal(vault.TemplateAddress, vaultArgs) + vestingAddress, vaultAddress := generateAddresses(m, keys, amount) // output addresses fmt.Printf("Vesting address: %s\nVault address: %s\n", @@ -96,6 +77,31 @@ var verifyCmd = &cobra.Command{ }, } +// generateAddresses generates vesting and vault addresses based on the provided parameters. +func generateAddresses(m uint8, keys []core.PublicKey, amount uint64) (core.Address, core.Address) { + vestingArgs := &multisig.SpawnArguments{ + Required: m, + PublicKeys: keys, + } + if int(vestingArgs.Required) > len(vestingArgs.PublicKeys) { + log.Fatalf("requires more signatures (%d) than public keys (%d) in the wallet\n", + vestingArgs.Required, + len(vestingArgs.PublicKeys), + ) + } + vestingAddress := core.ComputePrincipal(vesting.TemplateAddress, vestingArgs) + vaultArgs := &vault.SpawnArguments{ + Owner: vestingAddress, + TotalAmount: amount, + InitialUnlockAmount: amount / 4, + VestingStart: types.LayerID(constants.VestStart), + VestingEnd: types.LayerID(constants.VestEnd), + } + vaultAddress := core.ComputePrincipal(vault.TemplateAddress, vaultArgs) + + return vestingAddress, vaultAddress +} + func init() { rootCmd.AddCommand(genesisCmd) genesisCmd.AddCommand(verifyCmd) From 62582d230a468da6b054d266a66898e48ab6ac23 Mon Sep 17 00:00:00 2001 From: Noam Nelke Date: Tue, 6 Aug 2024 16:59:02 +0300 Subject: [PATCH 2/3] support a csv with account definitions as input --- cmd/genesis.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 6 deletions(-) diff --git a/cmd/genesis.go b/cmd/genesis.go index 9a38b3b..28ab9b5 100644 --- a/cmd/genesis.go +++ b/cmd/genesis.go @@ -2,9 +2,12 @@ package cmd import ( "crypto/ed25519" + "encoding/csv" "encoding/hex" "fmt" "log" + "os" + "strconv" "github.com/spacemeshos/economics/constants" "github.com/spacemeshos/go-spacemesh/common/types" @@ -54,10 +57,10 @@ var verifyCmd = &cobra.Command{ } // next collect multisig params - m := uint8(1) + requiredSignatures := uint8(1) if len(keys) > 1 { fmt.Printf("Enter number of required signatures (between 1 and %d): ", len(keys)) - _, err := fmt.Scanln(&m) + _, err := fmt.Scanln(&requiredSignatures) cobra.CheckErr(err) } @@ -68,7 +71,7 @@ var verifyCmd = &cobra.Command{ cobra.CheckErr(err) amount *= constants.OneSmesh - vestingAddress, vaultAddress := generateAddresses(m, keys, amount) + vestingAddress, vaultAddress := generateAddresses(requiredSignatures, keys, amount) // output addresses fmt.Printf("Vesting address: %s\nVault address: %s\n", @@ -77,11 +80,106 @@ var verifyCmd = &cobra.Command{ }, } +var processCSVCmd = &cobra.Command{ + Use: "csv", + Short: "Process a CSV file with account definitions (name, amount, key1, key2, key3, key4, key5, required_signatures)", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + csvFilePath, _ := cmd.Flags().GetString("file") + if csvFilePath == "" { + fmt.Println("CSV file path is required") + return + } + + file, err := os.Open(csvFilePath) + if err != nil { + fmt.Printf("Failed to open CSV file: %v\n", err) + return + } + defer file.Close() + + reader := csv.NewReader(file) + records, err := reader.ReadAll() + if err != nil { + fmt.Printf("Failed to read CSV file: %v\n", err) + return + } + + // Process each record + for i, record := range records { + if i == 0 { + // Add new columns to the header + record = append(record, "vesting_address", "vault_address") + records[i] = record + continue + } + + // Extract account details from the record + name := record[0] + amountStr := record[1] + keysStr := record[2:7] + requiredSignaturesStr := record[7] + + // Convert amount and requiredSignatures to appropriate types + amount, err := strconv.ParseUint(amountStr, 10, 64) + if err != nil { + log.Fatalf("Error: amount %s for %s on line %d is invalid", amountStr, name, i) + } + amount *= constants.OneSmesh + + requiredSignatures64, err := strconv.ParseUint(requiredSignaturesStr, 10, 8) + if err != nil { + log.Fatalf("Error: required signatures %s for %s on line %d is invalid", requiredSignaturesStr, name, i) + } + requiredSignatures := uint8(requiredSignatures64) + + // Collect the keys + var publicKeys []core.PublicKey + for _, keyStr := range keysStr { + if len(keyStr) == 0 { + break + } + keyBytes, err := hex.DecodeString(keyStr) + if err != nil || len(keyBytes) != ed25519.PublicKeySize { + log.Fatalf("Error: key %s for %s on line %s is unreadable", keyStr, name, i) + } + var pubKey core.PublicKey + copy(pubKey[:], keyBytes) + publicKeys = append(publicKeys, pubKey) + } + + // Generate vesting and vault addresses + vestingAddress, vaultAddress := generateAddresses(requiredSignatures, publicKeys, amount) + + // Append new addresses to the record + record = append(record, vestingAddress.String(), vaultAddress.String()) + records[i] = record + } + + // Write updated records to a new CSV file + outputFile, err := os.Create("updated_" + csvFilePath) + if err != nil { + fmt.Printf("Failed to create output CSV file: %v\n", err) + return + } + defer outputFile.Close() + + writer := csv.NewWriter(outputFile) + err = writer.WriteAll(records) + if err != nil { + fmt.Printf("Failed to write to output CSV file: %v\n", err) + return + } + + fmt.Println("CSV file processed successfully") + }, +} + // generateAddresses generates vesting and vault addresses based on the provided parameters. -func generateAddresses(m uint8, keys []core.PublicKey, amount uint64) (core.Address, core.Address) { +func generateAddresses(requiredSignatures uint8, publicKeys []core.PublicKey, amount uint64) (core.Address, core.Address) { vestingArgs := &multisig.SpawnArguments{ - Required: m, - PublicKeys: keys, + Required: requiredSignatures, + PublicKeys: publicKeys, } if int(vestingArgs.Required) > len(vestingArgs.PublicKeys) { log.Fatalf("requires more signatures (%d) than public keys (%d) in the wallet\n", @@ -105,4 +203,6 @@ func generateAddresses(m uint8, keys []core.PublicKey, amount uint64) (core.Addr func init() { rootCmd.AddCommand(genesisCmd) genesisCmd.AddCommand(verifyCmd) + processCSVCmd.Flags().StringP("file", "f", "", "Path to a CSV file with headings: name, amount, key1, key2, key3, key4, key5, required_signatures") + genesisCmd.AddCommand(processCSVCmd) } From 1f0e592b884d734d552edffd3a900585ac068d9c Mon Sep 17 00:00:00 2001 From: Noam Nelke Date: Tue, 6 Aug 2024 17:12:54 +0300 Subject: [PATCH 3/3] fix tests --- cmd/genesis.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cmd/genesis.go b/cmd/genesis.go index 28ab9b5..0a507e0 100644 --- a/cmd/genesis.go +++ b/cmd/genesis.go @@ -81,9 +81,10 @@ var verifyCmd = &cobra.Command{ } var processCSVCmd = &cobra.Command{ - Use: "csv", - Short: "Process a CSV file with account definitions (name, amount, key1, key2, key3, key4, key5, required_signatures)", - Args: cobra.MaximumNArgs(1), + Use: "csv", + Short: "Process a CSV file with account definitions " + + "(name, amount, key1, key2, key3, key4, key5, required_signatures)", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { csvFilePath, _ := cmd.Flags().GetString("file") if csvFilePath == "" { @@ -141,7 +142,7 @@ var processCSVCmd = &cobra.Command{ } keyBytes, err := hex.DecodeString(keyStr) if err != nil || len(keyBytes) != ed25519.PublicKeySize { - log.Fatalf("Error: key %s for %s on line %s is unreadable", keyStr, name, i) + log.Fatalf("Error: key %s for %s on line %d is unreadable", keyStr, name, i) } var pubKey core.PublicKey copy(pubKey[:], keyBytes) @@ -176,7 +177,11 @@ var processCSVCmd = &cobra.Command{ } // generateAddresses generates vesting and vault addresses based on the provided parameters. -func generateAddresses(requiredSignatures uint8, publicKeys []core.PublicKey, amount uint64) (core.Address, core.Address) { +func generateAddresses( + requiredSignatures uint8, + publicKeys []core.PublicKey, + amount uint64, +) (core.Address, core.Address) { vestingArgs := &multisig.SpawnArguments{ Required: requiredSignatures, PublicKeys: publicKeys, @@ -203,6 +208,7 @@ func generateAddresses(requiredSignatures uint8, publicKeys []core.PublicKey, am func init() { rootCmd.AddCommand(genesisCmd) genesisCmd.AddCommand(verifyCmd) - processCSVCmd.Flags().StringP("file", "f", "", "Path to a CSV file with headings: name, amount, key1, key2, key3, key4, key5, required_signatures") + processCSVCmd.Flags().StringP("file", "f", "", + "Path to a CSV file with headings: name, amount, key1, key2, key3, key4, key5, required_signatures") genesisCmd.AddCommand(processCSVCmd) }