diff --git a/cmd/account/account.go b/cmd/account/account.go index 4782f8bb5..dd8a9d32a 100644 --- a/cmd/account/account.go +++ b/cmd/account/account.go @@ -1,19 +1,29 @@ package account import ( - "encoding/base64" "encoding/hex" "fmt" "github.com/spf13/cobra" - slip10 "github.com/zoobc/zoo-slip10" "github.com/zoobc/zoobc-core/common/crypto" "github.com/zoobc/zoobc-core/common/model" "github.com/zoobc/zoobc-core/common/transaction" "github.com/zoobc/zoobc-core/common/util" ) +type ( + // GeneratorCommands represent struct of account generator commands + GeneratorCommands struct { + Signature crypto.SignatureInterface + TransactionUtil transaction.UtilInterface + } + // RunCommand represent of output function from account generator commands + RunCommand func(ccmd *cobra.Command, args []string) +) + var ( + accountGeneratorInstance *GeneratorCommands + accountCmd = &cobra.Command{ Use: "account", Short: "account is a developer cli tools to generate account.", @@ -21,109 +31,147 @@ var ( running 'zoobc account generate' will show create an account detail with its public key and private key both in bytes and hex representation + the secret phrase `, - Run: func(cmd *cobra.Command, args []string) { - generateRandomAccount() - }, } - - randomAccountCmd = &cobra.Command{ - Use: "random", - Short: "random defines to generate random account.", - Run: func(cmd *cobra.Command, args []string) { - generateRandomAccount() - }, + ed25519AccountCmd = &cobra.Command{ + Use: "ed25519", + Short: "Generate account using ed25519 algorithm. This is the default zoobc account", } - - fromSeedCmd = &cobra.Command{ - Use: "from-seed", - Short: "from-seed defines to generate account from provided seed.", - Run: func(cmd *cobra.Command, args []string) { - generateAccountFromSeed(signatureType, seed) - }, + bitcoinAccuntCmd = &cobra.Command{ + Use: "bitcoin", + Short: "Generate account based on Bitcoin signature that using Elliptic Curve Digital Signature Algorithm", } - multiSigCmd = &cobra.Command{ Use: "multisig", Aliases: []string{"musig", "ms"}, SuggestFor: []string{"mul", "multisignature", "multi-signature"}, - Short: "multisig allow to generate multi sig account", + Short: "Multisig allow to generate multi sig account", Long: "multisig allow to generate multi sig account address" + "provides account addresses, nonce, and minimum assignment", - Run: func(cmd *cobra.Command, args []string) { - info := &model.MultiSignatureInfo{ - MinimumSignatures: multisigMinimSigs, - Nonce: multiSigNonce, - Addresses: multisigAddresses, - } - address, err := (&transaction.Util{}).GenerateMultiSigAddress(info) - if err != nil { - fmt.Println(err.Error()) - } else { - fmt.Println(address) - } - - }, } ) func init() { - accountCmd.PersistentFlags().Int32Var( - &signatureType, - "signature-type", - int32(model.SignatureType_DefaultSignature), - "signature-type that provide type of signature want to use to generate the account", + // ed25519 + ed25519AccountCmd.Flags().StringVar(&seed, "seed", "", "Seed that is used to generate the account") + ed25519AccountCmd.Flags().BoolVar(&ed25519UseSlip10, "use-slip10", false, "use slip10 to generate ed25519 private key") + // bitcoin + bitcoinAccuntCmd.Flags().StringVar(&seed, "seed", "", "Seed that is used to generate the account") + bitcoinAccuntCmd.Flags().Int32Var( + &bitcoinPrivateKeyLength, + "private-key-length", + int32(model.PrivateKeyBytesLength_PrivateKey256Bits), + "The length of private key Bitcoin want to generate. supported format are 32, 48 & 64 length", + ) + bitcoinAccuntCmd.Flags().Int32Var( + &bitcoinPublicKeyFormat, + "public-key-format", + int32(model.BitcoinPublicKeyFormat_PublicKeyFormatCompressed), + "Defines the format of public key Bitcoin want to generate. 0 for compressed format & 1 for uncompressed format", ) - accountCmd.AddCommand(randomAccountCmd) - - fromSeedCmd.Flags().StringVar(&seed, "seed", "", "Seed that is used to generate the account") - fromSeedCmd.Flags().BoolVar(&hd, "hd", true, "--hd allow to generate account HD") - accountCmd.AddCommand(fromSeedCmd) - // multisig multiSigCmd.Flags().StringSliceVar(&multisigAddresses, "addresses", []string{}, "addresses that provides") multiSigCmd.Flags().Uint32Var(&multisigMinimSigs, "min-sigs", 0, "min-sigs that provide minimum signs") multiSigCmd.Flags().Int64Var(&multiSigNonce, "nonce", 0, "nonce that provides") - accountCmd.AddCommand(multiSigCmd) } // Commands will return the main generate account cmd func Commands() *cobra.Command { + if accountGeneratorInstance == nil { + accountGeneratorInstance = &GeneratorCommands{ + Signature: &crypto.Signature{}, + TransactionUtil: &transaction.Util{}, + } + } + ed25519AccountCmd.Run = accountGeneratorInstance.GenerateEd25519Account() + accountCmd.AddCommand(ed25519AccountCmd) + bitcoinAccuntCmd.Run = accountGeneratorInstance.GenerateBitcoinAccount() + accountCmd.AddCommand(bitcoinAccuntCmd) + multiSigCmd.Run = accountGeneratorInstance.GenerateMultiSignatureAccount() + accountCmd.AddCommand(multiSigCmd) return accountCmd -} -func generateRandomAccount() { - seed = util.GetSecureRandomSeed() - generateAccountFromSeed(signatureType, seed) } -func generateAccountFromSeed(signatureType int32, seed string) { - var ( - signature = crypto.Signature{} - seedBytes, privateKey, publicKey []byte - publicKeyString, address string - err error - slip10Key *slip10.Key - ) - - if hd { +// GenerateMultiSignatureAccount to generate address for multi signature transaction +func (gc *GeneratorCommands) GenerateMultiSignatureAccount() RunCommand { + return func(cmd *cobra.Command, args []string) { + info := &model.MultiSignatureInfo{ + MinimumSignatures: multisigMinimSigs, + Nonce: multiSigNonce, + Addresses: multisigAddresses, + } + address, err := gc.TransactionUtil.GenerateMultiSigAddress(info) + if err != nil { + fmt.Println(err.Error()) + } else { + fmt.Println(address) + } + } +} - seedBytes = slip10.NewSeed(seed, slip10.DefaultPassword) - slip10Key, err = slip10.DeriveForPath(slip10.StellarPrimaryAccountPath, seedBytes) +// GenerateEd25519Account to generate ed25519 account +func (gc *GeneratorCommands) GenerateEd25519Account() RunCommand { + return func(ccmd *cobra.Command, args []string) { + if seed == "" { + seed = util.GetSecureRandomSeed() + } + var ( + signatureType = model.SignatureType_DefaultSignature + privateKey, publicKey, publicKeyString, address, err = gc.Signature.GenerateAccountFromSeed( + signatureType, + seed, + ed25519UseSlip10, + ) + ) if err != nil { panic(err) } + PrintAccount( + int32(signatureType), + seed, + publicKeyString, + address, + privateKey, + publicKey, + ) + } +} - privateKey = slip10Key.Key - publicKey, _ = slip10Key.PublicKey() - publicKeyString = base64.StdEncoding.EncodeToString(publicKey) - address, _ = crypto.NewEd25519Signature().GetAddressFromPublicKey(publicKey) - } else { - privateKey, publicKey, publicKeyString, address, err = signature.GenerateAccountFromSeed(model.SignatureType(signatureType), seed) +// GenerateBitcoinAccount to generate bitcoin account +func (gc *GeneratorCommands) GenerateBitcoinAccount() RunCommand { + return func(ccmd *cobra.Command, args []string) { + if seed == "" { + seed = util.GetSecureRandomSeed() + } + var ( + signatureType = model.SignatureType_BitcoinSignature + privateKey, publicKey, publicKeyString, address, err = gc.Signature.GenerateAccountFromSeed( + signatureType, + seed, + model.PrivateKeyBytesLength(bitcoinPrivateKeyLength), + model.BitcoinPublicKeyFormat(bitcoinPublicKeyFormat), + ) + ) if err != nil { panic(err) } + PrintAccount( + int32(signatureType), + seed, + publicKeyString, + address, + privateKey, + publicKey, + ) } +} +// PrintAccount print out the generated account +func PrintAccount( + signatureType int32, + seed, publicKeyString, address string, + privateKey, publicKey []byte, +) { fmt.Printf("signature type: %s\n", model.SignatureType_name[signatureType]) fmt.Printf("seed: %s\n", seed) fmt.Printf("public key hex: %s\n", hex.EncodeToString(publicKey)) diff --git a/cmd/account/const.go b/cmd/account/const.go index d199956b2..f49806b09 100644 --- a/cmd/account/const.go +++ b/cmd/account/const.go @@ -1,10 +1,14 @@ package account var ( - seed string + seed string + // ed25519 + ed25519UseSlip10 bool + // bitcoin + bitcoinPrivateKeyLength int32 + bitcoinPublicKeyFormat int32 + // multisig multisigAddresses []string multisigMinimSigs uint32 multiSigNonce int64 - signatureType int32 - hd bool ) diff --git a/cmd/readme.md b/cmd/readme.md index 395c7ac11..b16b0c100 100644 --- a/cmd/readme.md +++ b/cmd/readme.md @@ -16,6 +16,11 @@ Command line interface to as a utility tools to develop the zoobc system. - example: `go run main.go account generate` will generate account to use. +### See more help about commands +- `go run main --help` to see available commands and flags +- `go run main {command} --help` to see to see available subcommands and flags +- `go run main {command} {subcommand} --help` to see to see available subcommands and flags of subcommand + ### Transaction general flag - `--output` to provide generated ouput type. Example: `--ouput bytes` - `--version` to provide version of transaction. Example: `--version 1` @@ -84,27 +89,16 @@ go run main.go generate transaction remove-account-dataset --timestamp 125789400 go run main.go generate block fake-blocks --numberOfBlocks=1000 --blocksmithSecretPhrase='sprinkled sneak species pork outpost thrift unwind cheesy vexingly dizzy neurology neatness' --out='../resource/zoobc.db' ``` -### Account Generating Randomly +### Account Generate Using Ed25519 Algorithm -``` -go run main.go generate account random +```bash +go run main.go generate account ed25519 --seed "concur vocalist rotten busload gap quote stinging undiluted surfer goofiness deviation starved" --use-slip10 ``` -### Account Generating From Seed -```bash -Flags: - --hd --hd allow to generate account HD (default true) - -h, --help help for from-seed - --seed string Seed that is used to generate the account +### Account Generate Using Bitcoin Algorithm -Global Flags: - --signature-type int32 signature-type that provide type of signature want to use to generate the account -``` -Example: ```bash -go run main.go generate account from-seed --seed "concur v -ocalist rotten busload gap quote stinging undiluted surfer go -ofiness deviation starved" +go run main.go generate account bitcoin --seed "concur vocalist rotten busload gap quote stinging undiluted surfer goofiness deviation starved" --private-key-length 32 --public-key-format 1 ### Genesis Generate From cmd/genesisblock/preRegisteredNodes.json ``` @@ -114,11 +108,6 @@ ofiness deviation starved" go run main.go generate account multisig --addresses "BCZnSfqpP5tqFQlMTYkDeBVFWnbyVK7vLr5ORFpTjgtN" --addresses "BCZD_VxfO2S9aziIL3cn_cXW7uPDVPOrnXuP98GEAUC7" --addresses "BCZKLvgUYZ1KKx-jtF9KoJskjVPvB9jpIjfzzI6zDW0J" —-min-sigs=2 --nonce=3 ``` -### Account Generate with spesific signature type -``` -go run main.go generate account random --signature-type 1 -``` - go run main.go genesis generate outputs cmd/genesis.go.new and cmd/cluster_config.json diff --git a/common/crypto/ed25519.go b/common/crypto/ed25519.go index 47f74dfbe..d481e9675 100644 --- a/common/crypto/ed25519.go +++ b/common/crypto/ed25519.go @@ -1,8 +1,10 @@ package crypto import ( + "bytes" "encoding/base64" + slip10 "github.com/zoobc/zoo-slip10" "github.com/zoobc/zoobc-core/common/blocker" "github.com/zoobc/zoobc-core/common/util" "golang.org/x/crypto/ed25519" @@ -37,6 +39,32 @@ func (*Ed25519Signature) GetPrivateKeyFromSeed(seed string) []byte { return ed25519.NewKeyFromSeed(seedHash[:]) } +// GetPrivateKeyFromSeedUseSlip10 generate privite key form seed using slip10, this private used by hdwallet +// NOTE: currently this private cannot use to sign message using golang ed25519, +// The output private key is first 32 bytes from private key golang ed25519 +func (*Ed25519Signature) GetPrivateKeyFromSeedUseSlip10(seed string) ([]byte, error) { + var ( + seedBytes = slip10.NewSeed(seed, slip10.DefaultPassword) + slip10Key, err = slip10.DeriveForPath(slip10.StellarPrimaryAccountPath, seedBytes) + ) + if err != nil { + return nil, err + } + return slip10Key.Key, nil +} + +// GetPublicKeyFromPrivateKeyUseSlip10 get pubic key from slip10 private key +func (*Ed25519Signature) GetPublicKeyFromPrivateKeyUseSlip10(privateKey []byte) ([]byte, error) { + var ( + reader = bytes.NewReader(privateKey) + publicKey, _, err = ed25519.GenerateKey(reader) + ) + if err != nil { + return nil, err + } + return publicKey, nil +} + // GetPublicKeyFromSeed Get the raw public key corresponding to a seed (secret phrase) func (es *Ed25519Signature) GetPublicKeyFromSeed(seed string) []byte { // Get the private key from the seed diff --git a/common/crypto/ed25519_test.go b/common/crypto/ed25519_test.go index dd9ebd23b..f4f60c021 100644 --- a/common/crypto/ed25519_test.go +++ b/common/crypto/ed25519_test.go @@ -261,3 +261,39 @@ func TestEd25519Signature_GetPrivateKeyFromSeed(t *testing.T) { }) } } + +func TestEd25519Signature_GetPrivateKeyFromSeedUseSlip10(t *testing.T) { + type args struct { + seed string + } + tests := []struct { + name string + e *Ed25519Signature + args args + want []byte + wantErr bool + }{ + { + name: "wantSuccess", + args: args{ + seed: ed25519MockSeed, + }, + want: []byte{28, 187, 31, 185, 115, 15, 68, 41, 91, 146, 30, 50, 233, 74, 207, 177, 84, 58, 87, + 180, 69, 50, 232, 116, 223, 54, 71, 100, 177, 196, 171, 106}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Ed25519Signature{} + got, err := e.GetPrivateKeyFromSeedUseSlip10(tt.args.seed) + if (err != nil) != tt.wantErr { + t.Errorf("Ed25519Signature.GetPrivateKeyFromSeedUseSlip10() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Ed25519Signature.GetPrivateKeyFromSeedUseSlip10() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/common/crypto/signature.go b/common/crypto/signature.go index 588a932e0..a272637db 100644 --- a/common/crypto/signature.go +++ b/common/crypto/signature.go @@ -16,9 +16,9 @@ type ( SignByNode(payload []byte, nodeSeed string) []byte VerifySignature(payload, signature []byte, accountAddress string) error VerifyNodeSignature(payload, signature []byte, nodePublicKey []byte) bool - GenerateAccountFromSeed(signatureType model.SignatureType, seed string) ( + GenerateAccountFromSeed(signatureType model.SignatureType, seed string, optionalParams ...interface{}) ( privateKey, publicKey []byte, - publickKeyString, address string, + publicKeyString, address string, err error, ) } @@ -170,38 +170,71 @@ func (*Signature) VerifyNodeSignature(payload, signature, nodePublicKey []byte) } // GenerateAccountFromSeed to generate account based on provided seed -func (*Signature) GenerateAccountFromSeed(signatureType model.SignatureType, seed string) ( +func (*Signature) GenerateAccountFromSeed(signatureType model.SignatureType, seed string, optionalParams ...interface{}) ( privateKey, publicKey []byte, - publickKeyString, address string, + publicKeyString, address string, err error, ) { switch signatureType { case model.SignatureType_DefaultSignature: - var ed25519Signature = NewEd25519Signature() - privateKey = ed25519Signature.GetPrivateKeyFromSeed(seed) - publicKey, err = ed25519Signature.GetPublicKeyFromPrivateKey(privateKey) - if err != nil { - return nil, nil, "", "", err + var ( + ed25519Signature = NewEd25519Signature() + useSlip10, ok bool + ) + if len(optionalParams) != 0 { + useSlip10, ok = optionalParams[0].(bool) + if !ok { + return nil, nil, "", "", blocker.NewBlocker(blocker.AppErr, "failedAssertType") + } } - publickKeyString = ed25519Signature.GetPublicKeyString(publicKey) + if useSlip10 { + privateKey, err = ed25519Signature.GetPrivateKeyFromSeedUseSlip10(seed) + if err != nil { + return nil, nil, "", "", err + } + publicKey, err = ed25519Signature.GetPublicKeyFromPrivateKeyUseSlip10(privateKey) + if err != nil { + return nil, nil, "", "", err + } + } else { + privateKey = ed25519Signature.GetPrivateKeyFromSeed(seed) + publicKey, err = ed25519Signature.GetPublicKeyFromPrivateKey(privateKey) + if err != nil { + return nil, nil, "", "", err + } + } + publicKeyString = ed25519Signature.GetPublicKeyString(publicKey) address, err = ed25519Signature.GetAddressFromPublicKey(publicKey) if err != nil { return nil, nil, "", "", err } - return privateKey, publicKey, publickKeyString, address, nil + return privateKey, publicKey, publicKeyString, address, nil case model.SignatureType_BitcoinSignature: var ( bitcoinSignature = NewBitcoinSignature(DefaultBitcoinNetworkParams(), DefaultBitcoinCurve()) - privKey, err = bitcoinSignature.GetPrivateKeyFromSeed(seed, DefaultBitcoinPrivateKeyLength()) + privateKeyLength = DefaultBitcoinPrivateKeyLength() + publicKeyFormat = DefaultBitcoinPublicKeyFormat() + ok bool ) + if len(optionalParams) >= 2 { + privateKeyLength, ok = optionalParams[0].(model.PrivateKeyBytesLength) + if !ok { + return nil, nil, "", "", blocker.NewBlocker(blocker.AppErr, "failedAssertPrivateKeyLengthType") + } + publicKeyFormat, ok = optionalParams[1].(model.BitcoinPublicKeyFormat) + if !ok { + return nil, nil, "", "", blocker.NewBlocker(blocker.AppErr, "failedAssertPublicKeyFormatType") + } + } + privKey, err := bitcoinSignature.GetPrivateKeyFromSeed(seed, privateKeyLength) if err != nil { return nil, nil, "", "", err } privateKey = privKey.Serialize() publicKey, err = bitcoinSignature.GetPublicKeyFromSeed( seed, - DefaultBitcoinPublicKeyFormat(), - DefaultBitcoinPrivateKeyLength(), + publicKeyFormat, + privateKeyLength, ) if err != nil { return nil, nil, "", "", err @@ -210,11 +243,11 @@ func (*Signature) GenerateAccountFromSeed(signatureType model.SignatureType, see if err != nil { return nil, nil, "", "", err } - publickKeyString, err = bitcoinSignature.GetPublicKeyString(publicKey) + publicKeyString, err = bitcoinSignature.GetPublicKeyString(publicKey) if err != nil { return nil, nil, "", "", err } - return privateKey, publicKey, publickKeyString, address, nil + return privateKey, publicKey, publicKeyString, address, nil default: return nil, nil, "", "", blocker.NewBlocker( blocker.AppErr, diff --git a/common/crypto/signature_test.go b/common/crypto/signature_test.go index 152aec077..1bf0dde21 100644 --- a/common/crypto/signature_test.go +++ b/common/crypto/signature_test.go @@ -240,14 +240,14 @@ func TestSignature_GenerateAccountFromSeed(t *testing.T) { seed string } tests := []struct { - name string - s *Signature - args args - wantPrivateKey []byte - wantPublicKey []byte - wantPublickKeyString string - wantAddress string - wantErr bool + name string + s *Signature + args args + wantPrivateKey []byte + wantPublicKey []byte + wantPublicKeyString string + wantAddress string + wantErr bool }{ { name: "GenerateAccountFromSeed:success-{DefaultSignature}", @@ -260,9 +260,9 @@ func TestSignature_GenerateAccountFromSeed(t *testing.T) { 82, 224, 72, 239, 56, 139, 255, 81, 229, 184, 77, 80, 80, 39, 254, 173, 28, 169}, wantPublicKey: []byte{4, 38, 68, 24, 230, 247, 88, 220, 119, 124, 51, 149, 127, 214, 82, 224, 72, 239, 56, 139, 255, 81, 229, 184, 77, 80, 80, 39, 254, 173, 28, 169}, - wantPublickKeyString: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv/UeW4TVBQJ/6tHKk=", - wantAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", - wantErr: false, + wantPublicKeyString: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv/UeW4TVBQJ/6tHKk=", + wantAddress: "BCZEGOb3WNx3fDOVf9ZS4EjvOIv_UeW4TVBQJ_6tHKlE", + wantErr: false, }, { name: "GenerateAccountFromSeed:success-{BitcoinSignature}", @@ -274,9 +274,9 @@ func TestSignature_GenerateAccountFromSeed(t *testing.T) { 71, 172, 247, 140, 12, 13, 53, 119, 251, 233, 244, 212}, wantPublicKey: []byte{3, 82, 247, 192, 243, 36, 207, 71, 90, 3, 103, 220, 47, 115, 64, 15, 13, 59, 186, 231, 45, 42, 149, 73, 12, 5, 166, 141, 205, 177, 156, 77, 122}, - wantPublickKeyString: "0352f7c0f324cf475a0367dc2f73400f0d3bbae72d2a95490c05a68dcdb19c4d7a", - wantAddress: "12Ea6WAMZhFnfM5kjyfrfykqVWFcaWorQ8", - wantErr: false, + wantPublicKeyString: "0352f7c0f324cf475a0367dc2f73400f0d3bbae72d2a95490c05a68dcdb19c4d7a", + wantAddress: "12Ea6WAMZhFnfM5kjyfrfykqVWFcaWorQ8", + wantErr: false, }, { name: "GenerateAccountFromSeed:fiiled-{invalid-signature-type}", @@ -284,11 +284,11 @@ func TestSignature_GenerateAccountFromSeed(t *testing.T) { signatureType: model.SignatureType(-1), seed: "concur vocalist rotten busload gap quote stinging undiluted surfer goofiness deviation starved", }, - wantPrivateKey: nil, - wantPublicKey: nil, - wantPublickKeyString: "", - wantAddress: "", - wantErr: true, + wantPrivateKey: nil, + wantPublicKey: nil, + wantPublicKeyString: "", + wantAddress: "", + wantErr: true, }, } for _, tt := range tests { @@ -305,8 +305,8 @@ func TestSignature_GenerateAccountFromSeed(t *testing.T) { if !reflect.DeepEqual(gotPublicKey, tt.wantPublicKey) { t.Errorf("Signature.GenerateAccountFromSeed() gotPublicKey = %v, want %v", gotPublicKey, tt.wantPublicKey) } - if gotPublickKeyString != tt.wantPublickKeyString { - t.Errorf("Signature.GenerateAccountFromSeed() gotPublickKeyString = %v, want %v", gotPublickKeyString, tt.wantPublickKeyString) + if gotPublickKeyString != tt.wantPublicKeyString { + t.Errorf("Signature.GenerateAccountFromSeed() gotPublickKeyString = %v, want %v", gotPublickKeyString, tt.wantPublicKeyString) } if gotAddress != tt.wantAddress { t.Errorf("Signature.GenerateAccountFromSeed() gotAddress = %v, want %v", gotAddress, tt.wantAddress)