diff --git a/Makefile b/Makefile index ca3362038..1f031b461 100644 --- a/Makefile +++ b/Makefile @@ -140,12 +140,6 @@ bin/cql-minerd: -o bin/cql-minerd \ github.com/CovenantSQL/CovenantSQL/cmd/cql-minerd -bin/cql-utils: - $(GOBUILD) \ - -ldflags "$(ldflags_role_client_simple_log)" \ - -o bin/cql-utils \ - github.com/CovenantSQL/CovenantSQL/cmd/cql-utils - bin/cql: $(GOBUILD) \ -ldflags "$(ldflags_role_client_simple_log)" \ @@ -181,7 +175,7 @@ bp: bin/cqld.test bin/cqld miner: bin/cql-minerd.test bin/cql-minerd -client: bin/cql-utils bin/cql bin/cql.test bin/cql-fuse bin/cql-mysql-adapter bin/cql-faucet +client: bin/cql bin/cql.test bin/cql-fuse bin/cql-mysql-adapter bin/cql-faucet all: bp miner client @@ -191,5 +185,5 @@ clean: rm -f coverage.txt .PHONY: status start stop logs push push_testnet clean \ - bin/cqld.test bin/cqld bin/cql-minerd.test bin/cql-minerd bin/cql-utils \ + bin/cqld.test bin/cqld bin/cql-minerd.test bin/cql-minerd \ bin/cql bin/cql.test bin/cql-fuse bin/cql-mysql-adapter bin/cql-faucet diff --git a/client/README-zh.md b/client/README-zh.md index 1a7dd7401..32c1a50de 100644 --- a/client/README-zh.md +++ b/client/README-zh.md @@ -6,7 +6,7 @@ ```bash $ go get github.com/CovenantSQL/CovenantSQL/client -$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql-utils +$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql ``` 然后在你的 go 代码中 import 第一个 `client` 包。 @@ -18,14 +18,14 @@ $ go get github.com/CovenantSQL/CovenantSQL/cmd/cql-utils ### 生成默认的配置文件 -运行以下 `cql-utils` 命令,输入 master key(类似密码)来生成本地密钥对。等待几十秒,会在 `conf` 文件夹中,生成一个私钥文件和一个名为 `config.yaml` 的配置文件。 +运行以下 `cql` 命令,输入 master key(类似密码)来生成本地密钥对。等待几十秒,会在 `~/.cql` 文件夹中,生成一个私钥文件和一个名为 `config.yaml` 的配置文件。 ```bash -$ cql-utils -tool confgen -root conf +$ cql generate config Generating key pair... Enter master key(press Enter for default: ""): ⏎ -Private key file: conf/private.key +Private key file: ~/.cql/private.key Public key's hex: 025abec9b0072615170f4acf4a2fa1162a13864bb66bc3f140b29f6bf50ceafc75 Generated key pair. Generating nonce... @@ -35,7 +35,7 @@ nonce: {{1450338416 0 0 0} 26 0000002dd8bdb50ba0270642e4c4bc593c1630ef7784653f31 node id: 0000002dd8bdb50ba0270642e4c4bc593c1630ef7784653f311b3c3d6374e514 Generated nonce. Generating config file... -Generated nonce. +Generated config. ``` 有了配置文件之后,可以通过以下 go 代码来初始化 CovenantSQL 客户端: @@ -100,4 +100,4 @@ dsn, err = client.Create(meta) ### 完整示例 -在以下目录中有一个简单示例和复杂示例可以参考 [示例](https://github.com/CovenantSQL/CovenantSQL/tree/develop/client/_example) \ No newline at end of file +在以下目录中有一个简单示例和复杂示例可以参考 [示例](https://github.com/CovenantSQL/CovenantSQL/tree/develop/client/_example) diff --git a/client/README.md b/client/README.md index ba53a074a..16a9e4a24 100644 --- a/client/README.md +++ b/client/README.md @@ -6,7 +6,7 @@ Make sure that `$GOPATH/bin` is in your `$PATH` ```bash $ go get github.com/CovenantSQL/CovenantSQL/client -$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql-utils +$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql ``` and import `client` package if you want to use it in your code. @@ -18,14 +18,14 @@ You need to provide a config and a master key for initialization. The master key ### Generating Default Config File -Run `cql-utils` like below. Enter a master key (like a password) for generating local key pair. After that, it may take a few seconds with a private key file and config.yaml file generated in `conf` folder. +Run `cql` like below. Enter a master key (like a password) for generating local key pair. After that, it may take a few seconds with a private key file and config.yaml file generated in `~/.cql/` folder. ```bash -$ cql-utils -tool confgen -root conf +$ cql generate config Generating key pair... Enter master key(press Enter for default: ""): ⏎ -Private key file: conf/private.key +Private key file: ~/.cql/private.key Public key's hex: 025abec9b0072615170f4acf4a2fa1162a13864bb66bc3f140b29f6bf50ceafc75 Generated key pair. Generating nonce... @@ -35,7 +35,7 @@ nonce: {{1450338416 0 0 0} 26 0000002dd8bdb50ba0270642e4c4bc593c1630ef7784653f31 node id: 0000002dd8bdb50ba0270642e4c4bc593c1630ef7784653f311b3c3d6374e514 Generated nonce. Generating config file... -Generated nonce. +Generated config. ``` After you prepare your master key and config file, CovenantSQL client can be initialized by: diff --git a/client/driver.go b/client/driver.go index 898ce067d..ff3412433 100644 --- a/client/driver.go +++ b/client/driver.go @@ -285,6 +285,9 @@ func Drop(dsn string) (txHash hash.Hash, err error) { peerList.Delete(cfg.DatabaseID) + //TODO(laodouya) currently not supported + //err = errors.New("drop db current not support") + return } diff --git a/cmd/cql-utils/README-zh.md b/cmd/cql-utils/README-zh.md deleted file mode 100644 index 37a78bb72..000000000 --- a/cmd/cql-utils/README-zh.md +++ /dev/null @@ -1,34 +0,0 @@ -cql-utils 是 CovenantSQL 的一个命令行工具,具体用法如下。 - -## 安装 -下载 [最新发布版本](https://github.com/CovenantSQL/CovenantSQL/releases) 或直接从源码编译: -```bash -$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql-utils -``` -*保证 Golang 环境变量 `$GOPATH/bin` 已在 `$PATH` 中* - -## 使用 -### 生成公私钥对 - -``` -$ cql-utils -tool confgen -Enter master key(press Enter for default: ""): -⏎ -Private key file: private.key -Public key's hex: 03bc9e90e3301a2f5ae52bfa1f9e033cde81b6b6e7188b11831562bf5847bff4c0 -``` - -生成的 ~/.cql/private.key 文件即是使用主密码加密过的私钥文件,而输出到屏幕上的字符串就是使用十六进制进行编码的公钥。 - -### 使用私钥文件或公钥生成钱包地址 - -``` -$ cql-utils -tool addrgen -Enter master key(default: ""): -⏎ -wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 -$ cql-utils -tool addrgen -public 02f2707c1c6955a9019cd9d02ade37b931fbfa286a1163dfc1de965ec01a5c4ff8 -wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 -``` - -你也可以通过-private指定私钥文件,或者把上述的公钥十六进制编码字符串作为命令行参数来直接生成钱包地址。 diff --git a/cmd/cql-utils/README.md b/cmd/cql-utils/README.md deleted file mode 100644 index d9da3a7ad..000000000 --- a/cmd/cql-utils/README.md +++ /dev/null @@ -1,34 +0,0 @@ -cql-utils is a helper command of CovenantSQL - -## Install -Download [Latest Release](https://github.com/CovenantSQL/CovenantSQL/releases) or build from src: -```bash -$ go get github.com/CovenantSQL/CovenantSQL/cmd/cql-utils -``` -*make sure that `$GOPATH/bin` is in your `$PATH`* - -## Usage -### Generate Key Pair - -``` -$ cql-utils -tool confgen -Enter master key(press Enter for default: ""): -⏎ -Private key file: private.key -Public key's hex: 03bc9e90e3301a2f5ae52bfa1f9e033cde81b6b6e7188b11831562bf5847bff4c0 -``` - -The ~/.cql/private.key is your encrypted private key file, and the pubkey hex is your public key's hex. - -### Generate Wallet Address from existing Key - -``` -$ cql-utils -tool addrgen -Enter master key(default: ""): -⏎ -wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 -$ cql-utils -tool addrgen -public 02f2707c1c6955a9019cd9d02ade37b931fbfa286a1163dfc1de965ec01a5c4ff8 -wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 -``` - -You can generate your *wallet* address for test net according to your private key(default ~/.cql/private) or public key. diff --git a/cmd/cql-utils/addrgen.go b/cmd/cql-utils/addrgen.go deleted file mode 100644 index 3c9a9bd77..000000000 --- a/cmd/cql-utils/addrgen.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/hex" - "fmt" - "os" - - "github.com/CovenantSQL/CovenantSQL/crypto" - "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -func runAddrgen() { - var publicKey *asymmetric.PublicKey - - if publicKeyHex != "" { - publicKeyBytes, err := hex.DecodeString(publicKeyHex) - if err != nil { - log.WithError(err).Fatal("error converting hex") - } - publicKey, err = asymmetric.ParsePubKey(publicKeyBytes) - if err != nil { - log.WithError(err).Fatal("error converting public key") - } - } else if privateKeyFile != "" { - masterKey, err := readMasterKey() - if err != nil { - fmt.Printf("read master key failed: %v\n", err) - os.Exit(1) - } - privateKey, err := kms.LoadPrivateKey(privateKeyFile, []byte(masterKey)) - if err != nil { - log.WithError(err).Fatal("load private key file failed") - } - publicKey = privateKey.PubKey() - } else { - fmt.Println("privateKey path or publicKey hex is required for addrgen") - os.Exit(1) - } - - keyHash, err := crypto.PubKeyHash(publicKey) - if err != nil { - log.WithError(err).Fatal("unexpected error") - } - - fmt.Printf("wallet address: %s\n", keyHash.String()) -} diff --git a/cmd/cql-utils/confgen.go b/cmd/cql-utils/confgen.go deleted file mode 100644 index eb1e3d970..000000000 --- a/cmd/cql-utils/confgen.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "path" - - yaml "gopkg.in/yaml.v2" - - "github.com/CovenantSQL/CovenantSQL/conf/testnet" - "github.com/CovenantSQL/CovenantSQL/proto" - "github.com/CovenantSQL/CovenantSQL/utils" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -var ( - workingRoot string -) - -func init() { - flag.StringVar(&workingRoot, "root", "~/.cql", "confgen root is the working root directory containing all auto-generating keys and certifications") -} - -func runConfgen() { - workingRoot = utils.HomeDirExpand(workingRoot) - if workingRoot == "" { - log.Error("root directory is required for confgen") - os.Exit(1) - } - - privateKeyFileName := "private.key" - publicKeystoreFileName := "public.keystore" - privateKeyFile = path.Join(workingRoot, privateKeyFileName) - - askDeletePath(workingRoot) - - err := os.Mkdir(workingRoot, 0755) - if err != nil { - log.WithError(err).Error("unexpected error") - os.Exit(1) - } - - fmt.Println("Generating key pair...") - publicKey := runKeygen(privateKeyFile) - fmt.Println("Generated key pair.") - - fmt.Println("Generating nonce...") - nonce := noncegen(publicKey) - cliNodeID := proto.NodeID(nonce.Hash.String()) - fmt.Println("Generated nonce.") - - fmt.Println("Generating config file...") - // Load testnet config - testnetConfig := testnet.GetTestNetConfig() - // Add client config - testnetConfig.PrivateKeyFile = privateKeyFileName - testnetConfig.PubKeyStoreFile = publicKeystoreFileName - testnetConfig.ThisNodeID = cliNodeID - if testnetConfig.KnownNodes == nil { - testnetConfig.KnownNodes = make([]proto.Node, 0, 1) - } - testnetConfig.KnownNodes = append(testnetConfig.KnownNodes, proto.Node{ - ID: cliNodeID, - Role: proto.Client, - Addr: "0.0.0.0:15151", - PublicKey: publicKey, - Nonce: nonce.Nonce, - }) - - // Write config - out, err := yaml.Marshal(testnetConfig) - if err != nil { - log.WithError(err).Error("unexpected error") - os.Exit(1) - } - err = ioutil.WriteFile(path.Join(workingRoot, "config.yaml"), out, 0644) - if err != nil { - log.WithError(err).Error("unexpected error") - os.Exit(1) - } - fmt.Println("Generated nonce.") -} diff --git a/cmd/cql-utils/idminer.go b/cmd/cql-utils/idminer.go deleted file mode 100644 index caa526434..000000000 --- a/cmd/cql-utils/idminer.go +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/hex" - "flag" - "fmt" - "math" - "math/rand" - "os" - "runtime" - "time" - - "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - mine "github.com/CovenantSQL/CovenantSQL/pow/cpuminer" - "github.com/CovenantSQL/CovenantSQL/proto" - "github.com/CovenantSQL/CovenantSQL/utils" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -var ( - difficulty int -) - -func init() { - flag.IntVar(&difficulty, "difficulty", 24, "difficulty for miner to mine nodes and generating nonce") -} - -func runMiner() { - masterKey, err := readMasterKey() - if err != nil { - fmt.Printf("read master key failed: %v\n", err) - os.Exit(1) - } - - var publicKey *asymmetric.PublicKey - - if publicKeyHex != "" { - publicKeyBytes, err := hex.DecodeString(publicKeyHex) - if err != nil { - log.WithError(err).Fatal("error converting hex") - } - publicKey, err = asymmetric.ParsePubKey(publicKeyBytes) - if err != nil { - log.WithError(err).Fatal("error converting public key") - } - } else if privateKeyFile != "" { - privateKey, err := kms.LoadPrivateKey(privateKeyFile, []byte(masterKey)) - if err != nil { - log.WithError(err).Fatal("load private key file failed") - } - publicKey = privateKey.PubKey() - } - - cpuCount := runtime.NumCPU() - log.Infof("cpu: %#v\n", cpuCount) - nonceChs := make([]chan mine.NonceInfo, cpuCount) - stopChs := make([]chan struct{}, cpuCount) - - rand.Seed(time.Now().UnixNano()) - step := math.MaxUint64 / uint64(cpuCount) - - for i := 0; i < cpuCount; i++ { - nonceChs[i] = make(chan mine.NonceInfo) - stopChs[i] = make(chan struct{}) - go func(i int) { - miner := mine.NewCPUMiner(stopChs[i]) - nonceCh := nonceChs[i] - block := mine.MiningBlock{ - Data: publicKey.Serialize(), - NonceChan: nonceCh, - Stop: nil, - } - start := mine.Uint256{D: step*uint64(i) + uint64(rand.Uint32())} - log.Infof("miner #%#v start: %#v\n", i, start) - miner.ComputeBlockNonce(block, start, difficulty) - }(i) - } - - sig := <-utils.WaitForExit() - log.Infof("received signal %#v\n", sig) - for i := 0; i < cpuCount; i++ { - close(stopChs[i]) - } - - max := mine.NonceInfo{} - for i := 0; i < cpuCount; i++ { - newNonce := <-nonceChs[i] - if max.Difficulty < newNonce.Difficulty { - max = newNonce - } - } - - // verify result - log.Infof("verify result: %#v\n", kms.IsIDPubNonceValid(&proto.RawNodeID{Hash: max.Hash}, &max.Nonce, publicKey)) - - // print result - fmt.Printf("nonce: %v\n", max) - fmt.Printf("node id: %v\n", max.Hash.String()) -} diff --git a/cmd/cql-utils/keygen.go b/cmd/cql-utils/keygen.go deleted file mode 100644 index adbb4a6c1..000000000 --- a/cmd/cql-utils/keygen.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/hex" - "fmt" - - "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -func runKeygen(privateKeyPath string) *asymmetric.PublicKey { - - askDeletePath(privateKeyPath) - - masterKey, err := readMasterKey() - if err != nil { - log.WithError(err).Fatal("read master key failed") - } - - privateKey, _, err := asymmetric.GenSecp256k1KeyPair() - if err != nil { - log.WithError(err).Fatal("generate key pair failed") - } - - if err = kms.SavePrivateKey(privateKeyPath, privateKey, []byte(masterKey)); err != nil { - log.WithError(err).Fatal("save generated keypair failed") - } - - fmt.Printf("Private key file: %s\n", privateKeyPath) - fmt.Printf("Public key's hex: %s\n", hex.EncodeToString(privateKey.PubKey().Serialize())) - return privateKey.PubKey() -} diff --git a/cmd/cql-utils/keytool.go b/cmd/cql-utils/keytool.go deleted file mode 100644 index d96afcf64..000000000 --- a/cmd/cql-utils/keytool.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/hex" - "fmt" - "os" - - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -func runKeytool() { - masterKey, err := readMasterKey() - if err != nil { - fmt.Printf("read master key failed: %v\n", err) - os.Exit(1) - } - - privateKey, err := kms.LoadPrivateKey(privateKeyFile, []byte(masterKey)) - if err != nil { - log.WithError(err).Error("load private key failed") - } - - fmt.Printf("Public key's hex: %s\n", hex.EncodeToString(privateKey.PubKey().Serialize())) -} diff --git a/cmd/cql-utils/main.go b/cmd/cql-utils/main.go deleted file mode 100644 index 7a7d8210d..000000000 --- a/cmd/cql-utils/main.go +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "bufio" - "flag" - "fmt" - "os" - "runtime" - "strings" - "syscall" - - "golang.org/x/crypto/ssh/terminal" - - "github.com/CovenantSQL/CovenantSQL/utils" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -var ( - version = "unknown" - tool string - publicKeyHex string - privateKeyFile string - configFile string - skipMasterKey bool - showVersion bool -) - -const name = "cql-utils" - -func init() { - log.SetLevel(log.InfoLevel) - - flag.StringVar(&tool, "tool", "", "Tool type, miner, keytool, rpc, nonce, confgen, addrgen") - flag.StringVar(&publicKeyHex, "public", "", "Public key hex string to mine node id/nonce") - flag.StringVar(&privateKeyFile, "private", "~/.cql/private.key", "Private key file to generate/show") - flag.StringVar(&configFile, "config", "~/.cql/config.yaml", "Config file to use") - flag.BoolVar(&skipMasterKey, "skip-master-key", false, "Use empty master key") - flag.BoolVar(&showVersion, "version", false, "Show version information and exit") -} - -func main() { - flag.Parse() - if showVersion { - fmt.Printf("%v %v %v %v %v\n", - name, version, runtime.GOOS, runtime.GOARCH, runtime.Version()) - os.Exit(0) - } - log.Infof("cql-utils build: %#v\n", version) - - configFile = utils.HomeDirExpand(configFile) - privateKeyFile = utils.HomeDirExpand(privateKeyFile) - - switch tool { - case "miner": - if publicKeyHex == "" && privateKeyFile == "" { - // error - log.Error("publicKey or privateKey is required in miner mode") - os.Exit(1) - } - runMiner() - // Disable keygen independent call - //case "keygen": - // if privateKeyFile == "" { - // // error - // log.Error("privateKey path is required for keygen") - // os.Exit(1) - // } - // runKeygen() - case "keytool": - if privateKeyFile == "" { - // error - log.Error("privateKey path is required for keytool") - os.Exit(1) - } - runKeytool() - case "rpc": - runRPC() - case "nonce": - runNonce() - case "confgen": - runConfgen() - case "addrgen": - if privateKeyFile == "" && publicKeyHex == "" { - log.Error("privateKey path or publicKey hex is required for addrgen") - os.Exit(1) - } - runAddrgen() - default: - flag.Usage() - os.Exit(1) - } -} - -func readMasterKey() (string, error) { - if skipMasterKey { - return "", nil - } - fmt.Println("Enter master key(press Enter for default: \"\"): ") - bytePwd, err := terminal.ReadPassword(int(syscall.Stdin)) - fmt.Println() - return string(bytePwd), err -} - -func askDeletePath(path string) { - if _, err := os.Stat(path); err == nil { - reader := bufio.NewReader(os.Stdin) - fmt.Printf("\"%s\" already exists. \nDo you want to delete it? (y or n, press Enter for default n):\n", - path) - t, err := reader.ReadString('\n') - t = strings.Trim(t, "\n") - if err != nil { - log.WithError(err).Error("unexpected error") - os.Exit(1) - } - if strings.Compare(t, "y") == 0 || strings.Compare(t, "yes") == 0 { - err = os.RemoveAll(path) - if err != nil { - log.WithError(err).Error("unexpected error") - os.Exit(1) - } - } else { - os.Exit(0) - } - } -} diff --git a/cmd/cql-utils/noncegen.go b/cmd/cql-utils/noncegen.go deleted file mode 100644 index be8acad38..000000000 --- a/cmd/cql-utils/noncegen.go +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "encoding/hex" - "fmt" - "math/rand" - "os" - "runtime" - "time" - - "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - mine "github.com/CovenantSQL/CovenantSQL/pow/cpuminer" - "github.com/CovenantSQL/CovenantSQL/proto" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -func runNonce() { - var publicKey *asymmetric.PublicKey - - if publicKeyHex != "" { - publicKeyBytes, err := hex.DecodeString(publicKeyHex) - if err != nil { - log.WithError(err).Fatal("error converting hex") - } - publicKey, err = asymmetric.ParsePubKey(publicKeyBytes) - if err != nil { - log.WithError(err).Fatal("error converting public key") - } - } else if privateKeyFile != "" { - masterKey, err := readMasterKey() - if err != nil { - log.WithError(err).Error("read master key failed") - os.Exit(1) - } - privateKey, err := kms.LoadPrivateKey(privateKeyFile, []byte(masterKey)) - if err != nil { - log.WithError(err).Fatal("load private key file failed") - } - publicKey = privateKey.PubKey() - } else { - log.Fatalln("can neither convert public key nor load private key") - } - - noncegen(publicKey) -} - -func noncegen(publicKey *asymmetric.PublicKey) *mine.NonceInfo { - publicKeyBytes := publicKey.Serialize() - - cpuCount := runtime.NumCPU() - log.Infof("cpu: %#v\n", cpuCount) - stopCh := make(chan struct{}) - nonceCh := make(chan mine.NonceInfo) - - rand.Seed(time.Now().UnixNano()) - step := 256 / cpuCount - for i := 0; i < cpuCount; i++ { - go func(i int) { - startBit := i * step - position := startBit / 64 - shift := uint(startBit % 64) - log.Infof("position: %#v, shift: %#v, i: %#v", position, shift, i) - var start mine.Uint256 - if position == 0 { - start = mine.Uint256{A: uint64(1<= difficulty { - nonce := mine.NonceInfo{ - Nonce: j, - Difficulty: currentDifficulty, - Hash: currentHash, - } - nonceCh <- nonce - } - } - } - }(i) - } - - nonce := <-nonceCh - close(stopCh) - - // verify result - if !kms.IsIDPubNonceValid(&proto.RawNodeID{Hash: nonce.Hash}, &nonce.Nonce, publicKey) { - log.WithFields(log.Fields{ - "nonce": nonce, - "id": nonce.Hash.String(), - }).Fatal("invalid nonce") - } - - // print result - fmt.Printf("nonce: %v\n", nonce) - fmt.Printf("node id: %v\n", nonce.Hash.String()) - - return &nonce -} diff --git a/cmd/cql/README-zh.md b/cmd/cql/README-zh.md index 99ba7dbfb..b09a47eb5 100644 --- a/cmd/cql/README-zh.md +++ b/cmd/cql/README-zh.md @@ -11,16 +11,42 @@ $ go get github.com/CovenantSQL/CovenantSQL/cmd/cql ## 生成默认配置文件 -首先需要一个 config 文件和由你输入的主密码(master key)来初始化,其中主密码用来加密解密本地密钥对。使用 `cql-utils` 工具进行配置文件生成后,你可以在生成的配置文件目录下找到密钥文件。 +首先需要一个 config 文件和由你输入的主密码(master key)来初始化,其中主密码用来加密解密本地密钥对。使用 `cql generate config` 工具进行配置文件生成后,你可以在生成的配置文件目录下找到密钥文件。 -具体请参考: [cql-utils 使用文档](https://github.com/CovenantSQL/docs/blob/master/docs/development-cmd-cql-utils-zh.md#使用) 中配置文件及钱包地址生成相关章节。 +``` +$ cql generate config +Enter master key(press Enter for default: ""): +⏎ +Private key file: private.key +Public key's hex: 03bc9e90e3301a2f5ae52bfa1f9e033cde81b6b6e7188b11831562bf5847bff4c0 +``` + +生成的 ~/.cql/private.key 文件即是使用主密码加密过的私钥文件,而输出到屏幕上的字符串就是使用十六进制进行编码的公钥。 + +### 使用私钥文件生成钱包地址 + +``` +$ cql wallet +Enter master key(default: ""): +⏎ +wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 +``` + +你也可以通过-config指定配置文件, 来直接生成钱包地址。 + +``` +$ cql generate -config ~/.cql/config.yaml wallet +Enter master key(default: ""): +⏎ +wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 +``` ## 检查钱包余额 使用 `cql` 命令来检查钱包余额: ```bash -$ cql balance +$ cql wallet -balance all INFO[0000] ### Public Key ### 0388954cf083bb6bb2b9c7248849b57c76326296fcc0d69764fc61eedb5b8d820c diff --git a/cmd/cql/README.md b/cmd/cql/README.md index 94c211e58..c3904126d 100644 --- a/cmd/cql/README.md +++ b/cmd/cql/README.md @@ -11,16 +11,39 @@ $ go get github.com/CovenantSQL/CovenantSQL/cmd/cql ## Generating Default Config File -You need to provide a config and a master key for initialization. The master key is used to encrypt/decrypt local key pair. If you generate a config file with `cql-utils`, you can find the config file in the directory that `cql-utils` create. +You need to provide a config and a master key for initialization. The master key is used to encrypt/decrypt local key pair. If you generate a config file with `cql generate config`, you can find the config file in the directory `~/.cql`. -See: [cql-utils doc](https://github.com/CovenantSQL/CovenantSQL/tree/develop/cmd/cql-utils#usage) for config generation and get wallet address. +``` +$ cql generate config +Enter master key(press Enter for default: ""): +⏎ +Private key file: private.key +Public key's hex: 03bc9e90e3301a2f5ae52bfa1f9e033cde81b6b6e7188b11831562bf5847bff4c0 +``` + +The ~/.cql/private.key is your encrypted private key file, and the pubkey hex is your public key's hex. + +### Generate Wallet Address from existing Key + +``` +$ cql wallet +Enter master key(default: ""): +⏎ +wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 +$ cql generate -config ~/.cql/config.yaml wallet +Enter master key(default: ""): +⏎ +wallet address: 4jXvNvPHKNPU8Sncz5u5F5WSGcgXmzC1g8RuAXTCJzLsbF9Dsf9 +``` + +You can generate your *wallet* address for test net according to your private key(default ~/.cql/private). ## Check balance Use `cql` to check your wallet balance: ```bash -$ cql balance +$ cql wallet -balance all INFO[0000] ### Public Key ### 0388954cf083bb6bb2b9c7248849b57c76326296fcc0d69764fc61eedb5b8d820c diff --git a/cmd/cql/internal/cfg.go b/cmd/cql/internal/cfg.go index 6e9f5d214..e3319d851 100644 --- a/cmd/cql/internal/cfg.go +++ b/cmd/cql/internal/cfg.go @@ -29,6 +29,7 @@ import ( "github.com/CovenantSQL/CovenantSQL/conf" "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" "github.com/CovenantSQL/CovenantSQL/crypto/hash" + "github.com/CovenantSQL/CovenantSQL/crypto/kms" "github.com/CovenantSQL/CovenantSQL/utils" "github.com/CovenantSQL/CovenantSQL/utils/log" "github.com/sirupsen/logrus" @@ -71,7 +72,7 @@ func configInit() { Exit() } - ConsoleLog.Info("init config success") + ConsoleLog.WithField("path", configFile).Info("init config success") // TODO(leventeliu): discover more specific confirmation duration from config. We don't have // enough informations from config to do that currently, so just use a fixed and long enough @@ -134,3 +135,26 @@ func readMasterKey(skip bool) string { } return string(bytePwd) } + +func getPublic() *asymmetric.PublicKey { + //if config has public, use it + for _, node := range conf.GConf.KnownNodes { + if node.ID == conf.GConf.ThisNodeID { + if node.PublicKey != nil { + ConsoleLog.Infof("use public key in config file: %s", configFile) + return node.PublicKey + } + break + } + } + + //use config specific private key file(already init by configInit()) + ConsoleLog.Infof("generate public key directly from private key: %s", conf.GConf.PrivateKeyFile) + privateKey, err := kms.LoadPrivateKey(conf.GConf.PrivateKeyFile, []byte(password)) + if err != nil { + ConsoleLog.WithError(err).Error("load private key file failed") + SetExitStatus(1) + ExitIfErrors() + } + return privateKey.PubKey() +} diff --git a/cmd/cql/internal/generate.go b/cmd/cql/internal/generate.go new file mode 100644 index 000000000..fa0770ff9 --- /dev/null +++ b/cmd/cql/internal/generate.go @@ -0,0 +1,190 @@ +/* + * Copyright 2018-2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package internal + +import ( + "bufio" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/CovenantSQL/CovenantSQL/conf/testnet" + "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" + "github.com/CovenantSQL/CovenantSQL/crypto/kms" + "github.com/CovenantSQL/CovenantSQL/proto" + "github.com/CovenantSQL/CovenantSQL/utils" + yaml "gopkg.in/yaml.v2" +) + +// CmdGenerate is cql generate command entity. +var CmdGenerate = &Command{ + UsageLine: "cql generate [-config file] config/public", + Short: "generate config related file or keys", + Long: ` +Generate command can generate private.key and config.yaml for CovenantSQL. +e.g. + cql generate config +`, +} + +func init() { + CmdGenerate.Run = runGenerate + + addCommonFlags(CmdGenerate) +} + +func askDeletePath(path string) { + if fileinfo, err := os.Stat(path); err == nil { + if !fileinfo.IsDir() { + path = filepath.Dir(path) + } + reader := bufio.NewReader(os.Stdin) + fmt.Printf("\"%s\" already exists. \nDo you want to delete it? (y or n, press Enter for default n):\n", + path) + t, err := reader.ReadString('\n') + t = strings.Trim(t, "\n") + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + Exit() + } + if strings.EqualFold(t, "y") || strings.EqualFold(t, "yes") { + err = os.RemoveAll(path) + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + Exit() + } + } else { + Exit() + } + } +} + +func runGenerate(cmd *Command, args []string) { + if len(args) != 1 { + ConsoleLog.Error("Generate command need specific type as params") + SetExitStatus(1) + return + } + genType := args[0] + + switch genType { + case "config": + configGen() + case "public": + configInit() + publicKey := getPublic() + fmt.Printf("Public key's hex: %s\n", hex.EncodeToString(publicKey.Serialize())) + default: + cmd.Usage() + SetExitStatus(1) + return + } +} + +func configGen() { + workingRoot := utils.HomeDirExpand(configFile) + if workingRoot == "" { + ConsoleLog.Error("config directory is required for generate config") + SetExitStatus(1) + return + } + + if strings.HasSuffix(workingRoot, "config.yaml") { + workingRoot = filepath.Dir(workingRoot) + } + + askDeletePath(workingRoot) + + privateKeyFileName := "private.key" + privateKeyFile := path.Join(workingRoot, privateKeyFileName) + + err := os.Mkdir(workingRoot, 0755) + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + return + } + + fmt.Println("Generating key pair...") + + if password == "" { + password = readMasterKey(noPassword) + } + + privateKey, _, err := asymmetric.GenSecp256k1KeyPair() + if err != nil { + ConsoleLog.WithError(err).Error("generate key pair failed") + SetExitStatus(1) + return + } + + if err = kms.SavePrivateKey(privateKeyFile, privateKey, []byte(password)); err != nil { + ConsoleLog.WithError(err).Error("save generated keypair failed") + SetExitStatus(1) + return + } + + fmt.Printf("Private key file: %s\n", privateKeyFile) + fmt.Printf("Public key's hex: %s\n", hex.EncodeToString(privateKey.PubKey().Serialize())) + publicKey := privateKey.PubKey() + + fmt.Println("Generated key pair.") + + fmt.Println("Generating nonce...") + nonce := nonceGen(publicKey) + cliNodeID := proto.NodeID(nonce.Hash.String()) + fmt.Println("Generated nonce.") + + fmt.Println("Generating config file...") + // Load testnet config + testnetConfig := testnet.GetTestNetConfig() + // Add client config + testnetConfig.PrivateKeyFile = privateKeyFileName + testnetConfig.ThisNodeID = cliNodeID + if testnetConfig.KnownNodes == nil { + testnetConfig.KnownNodes = make([]proto.Node, 0, 1) + } + testnetConfig.KnownNodes = append(testnetConfig.KnownNodes, proto.Node{ + ID: cliNodeID, + Role: proto.Client, + Addr: "0.0.0.0:15151", + PublicKey: publicKey, + Nonce: nonce.Nonce, + }) + + // Write config + out, err := yaml.Marshal(testnetConfig) + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + return + } + err = ioutil.WriteFile(path.Join(workingRoot, "config.yaml"), out, 0644) + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + return + } + fmt.Println("Generated config.") + +} diff --git a/cmd/cql/internal/idminer.go b/cmd/cql/internal/idminer.go new file mode 100644 index 000000000..75567820f --- /dev/null +++ b/cmd/cql/internal/idminer.go @@ -0,0 +1,185 @@ +/* + * Copyright 2018-2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package internal + +import ( + "fmt" + "math" + "math/rand" + "runtime" + "time" + + "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" + "github.com/CovenantSQL/CovenantSQL/crypto/kms" + mine "github.com/CovenantSQL/CovenantSQL/pow/cpuminer" + "github.com/CovenantSQL/CovenantSQL/proto" + "github.com/CovenantSQL/CovenantSQL/utils" + "github.com/sirupsen/logrus" +) + +var ( + difficulty int + loop bool +) + +// CmdIDMiner is cql idminer command entity. +var CmdIDMiner = &Command{ + UsageLine: "cql idminer [-config file] [-difficulty number] [-loop false]", + Short: "calculate nonce and node id for config.yaml file", + Long: ` +IDMiner command can calculate legal node id and it's nonce. Default 24 difficulty and no endless loop. +e.g. + cql idminer -difficulty 24 + +If you want mining a good id, use: + cql idminer -config ~/.cql/config.yaml -loop -difficulty 24 +`, +} + +func init() { + CmdIDMiner.Run = runIDMiner + + addCommonFlags(CmdIDMiner) + CmdIDMiner.Flag.IntVar(&difficulty, "difficulty", 24, "difficulty for miner to mine nodes and generating nonce") + CmdIDMiner.Flag.BoolVar(&loop, "loop", false, "mining endless loop") +} + +func runIDMiner(cmd *Command, args []string) { + configInit() + + publicKey := getPublic() + + if loop { + nonceLoop(publicKey) + } else { + _ = nonceGen(publicKey) + } +} + +func nonceLoop(publicKey *asymmetric.PublicKey) { + cpuCount := runtime.NumCPU() + ConsoleLog.Infof("cpu: %#v\n", cpuCount) + nonceChs := make([]chan mine.NonceInfo, cpuCount) + stopChs := make([]chan struct{}, cpuCount) + + rand.Seed(time.Now().UnixNano()) + step := math.MaxUint64 / uint64(cpuCount) + + for i := 0; i < cpuCount; i++ { + nonceChs[i] = make(chan mine.NonceInfo) + stopChs[i] = make(chan struct{}) + go func(i int) { + miner := mine.NewCPUMiner(stopChs[i]) + nonceCh := nonceChs[i] + block := mine.MiningBlock{ + Data: publicKey.Serialize(), + NonceChan: nonceCh, + Stop: nil, + } + start := mine.Uint256{D: step*uint64(i) + uint64(rand.Uint32())} + ConsoleLog.Infof("miner #%#v start: %#v\n", i, start) + miner.ComputeBlockNonce(block, start, difficulty) + //TODO(laodouya) add wait group + }(i) + } + + sig := <-utils.WaitForExit() + ConsoleLog.Infof("received signal %#v\n", sig) + for i := 0; i < cpuCount; i++ { + close(stopChs[i]) + } + + max := mine.NonceInfo{} + for i := 0; i < cpuCount; i++ { + newNonce := <-nonceChs[i] + if max.Difficulty < newNonce.Difficulty { + max = newNonce + } + } + + // verify result + ConsoleLog.Infof("verify result: %#v\n", kms.IsIDPubNonceValid(&proto.RawNodeID{Hash: max.Hash}, &max.Nonce, publicKey)) + + // print result + fmt.Printf("nonce: %v\n", max) + fmt.Printf("node id: %v\n", max.Hash.String()) +} + +func nonceGen(publicKey *asymmetric.PublicKey) *mine.NonceInfo { + publicKeyBytes := publicKey.Serialize() + + cpuCount := runtime.NumCPU() + ConsoleLog.Infof("cpu: %#v\n", cpuCount) + stopCh := make(chan struct{}) + nonceCh := make(chan mine.NonceInfo) + + rand.Seed(time.Now().UnixNano()) + step := 256 / cpuCount + for i := 0; i < cpuCount; i++ { + go func(i int) { + startBit := i * step + position := startBit / 64 + shift := uint(startBit % 64) + ConsoleLog.Infof("position: %#v, shift: %#v, i: %#v", position, shift, i) + var start mine.Uint256 + if position == 0 { + start = mine.Uint256{A: uint64(1<= difficulty { + nonce := mine.NonceInfo{ + Nonce: j, + Difficulty: currentDifficulty, + Hash: currentHash, + } + nonceCh <- nonce + } + } + } + }(i) + } + + nonce := <-nonceCh + close(stopCh) + + // verify result + if !kms.IsIDPubNonceValid(&proto.RawNodeID{Hash: nonce.Hash}, &nonce.Nonce, publicKey) { + ConsoleLog.WithFields(logrus.Fields{ + "nonce": nonce, + "id": nonce.Hash.String(), + }).Fatal("invalid nonce") + } + + // print result + fmt.Printf("nonce: %v\n", nonce) + fmt.Printf("node id: %v\n", nonce.Hash.String()) + + return &nonce +} diff --git a/cmd/cql-utils/rpc.go b/cmd/cql/internal/rpc.go similarity index 76% rename from cmd/cql-utils/rpc.go rename to cmd/cql/internal/rpc.go index e2af31d7e..345294f0a 100644 --- a/cmd/cql-utils/rpc.go +++ b/cmd/cql/internal/rpc.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 The CovenantSQL Authors. + * Copyright 2018-2019 The CovenantSQL Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,10 @@ * limitations under the License. */ -package main +package internal import ( "encoding/json" - "flag" "fmt" "reflect" "strings" @@ -28,7 +27,6 @@ import ( bp "github.com/CovenantSQL/CovenantSQL/blockproducer" pi "github.com/CovenantSQL/CovenantSQL/blockproducer/interfaces" - "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/crypto" "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" "github.com/CovenantSQL/CovenantSQL/crypto/kms" @@ -37,7 +35,6 @@ import ( "github.com/CovenantSQL/CovenantSQL/rpc" "github.com/CovenantSQL/CovenantSQL/sqlchain" "github.com/CovenantSQL/CovenantSQL/types" - "github.com/CovenantSQL/CovenantSQL/utils/log" "github.com/CovenantSQL/CovenantSQL/worker" ) @@ -48,41 +45,51 @@ var ( route.SQLChainRPCName: &sqlchain.MuxService{}, route.BlockProducerRPCName: &bp.ChainRPCService{}, } - rpcName string - rpcEndpoint string - rpcReq string - rpcTxWaitConfirm bool + rpcName string + rpcEndpoint string + rpcReq string ) +// CmdRPC is cql rpc command entity. +var CmdRPC = &Command{ + UsageLine: "cql rpc [-config file] [-wait-tx-confirm] -name rpc_name -endpoint rpc_endpoint -req rpc_request", + Short: "make a rpc request", + Long: ` +Rpc command make a RPC request to server +e.g. + cql rpc -name 'MCC.QuerySQLChainProfile' \ + -rpc-endpoint 000000fd2c8f68d54d55d97d0ad06c6c0d91104e4e51a7247f3629cc2a0127cf \ + -rpc-req '{"DBID": "c8328272ba9377acdf1ee8e73b17f2b0f7430c798141080d0282195507eb94e7"}' +`, +} + type canSign interface { Sign(signer *asymmetric.PrivateKey) error } func init() { - flag.StringVar(&rpcName, "rpc", "", "rpc name to do test call") - flag.StringVar(&rpcEndpoint, "rpc-endpoint", "", "rpc endpoint to do test call") - flag.StringVar(&rpcReq, "rpc-req", "", "rpc request to do test call, in json format") - flag.BoolVar(&rpcTxWaitConfirm, "rpc-tx-wait-confirm", false, "wait for transaction confirmation") + CmdRPC.Run = runRPC + + addCommonFlags(CmdRPC) + addWaitFlag(CmdRPC) + + CmdRPC.Flag.StringVar(&rpcName, "name", "", "RPC name to do test call") + CmdRPC.Flag.StringVar(&rpcEndpoint, "endpoint", "", "RPC endpoint to do test call") + CmdRPC.Flag.StringVar(&rpcReq, "req", "", "RPC request to do test call, in json format") } -func runRPC() { - if configFile == "" { - // error - log.Fatal("config file path is required for rpc tool") - return - } +func runRPC(cmd *Command, args []string) { + configInit() + if rpcEndpoint == "" || rpcName == "" || rpcReq == "" { // error - log.Fatal("rpc payload is required for rpc tool") - return - } - - if err := client.Init(configFile, []byte("")); err != nil { - log.Fatalf("init rpc client failed: %v\n", err) + ConsoleLog.Error("rpc payload is required for rpc tool") + SetExitStatus(1) return } req, resp := resolveRPCEntities() + ExitIfErrors() if rpcName == route.MCCAddTx.String() { // special type of query @@ -94,7 +101,8 @@ func runRPC() { // fill the req with request body if err := json.Unmarshal([]byte(rpcReq), req); err != nil { - log.Fatalf("decode request body failed: %v\n", err) + ConsoleLog.WithError(err).Error("decode request body failed") + SetExitStatus(1) return } @@ -113,12 +121,14 @@ func runRPC() { // unwrapped tx, find account nonce field and set if err := fillTxNonce(tx); err != nil { - log.Fatalf("fill block producer transaction nonce failed: %v\n", err) + ConsoleLog.WithError(err).Error("fill block producer transaction nonce failed") + SetExitStatus(1) return } if err := checkAndSign(tx); err != nil { - log.Fatalf("sign transaction failed: %v\n", err) + ConsoleLog.WithError(err).Error("sign transaction failed") + SetExitStatus(1) return } } @@ -126,26 +136,27 @@ func runRPC() { // requires signature? if err := checkAndSign(req); err != nil { - log.Fatalf("sign request failed: %v\n", err) + ConsoleLog.WithError(err).Error("sign request failed") + SetExitStatus(1) return } - log.Info("sending request") + ConsoleLog.Info("sending request") spewCfg := spew.NewDefaultConfig() spewCfg.MaxDepth = 6 spewCfg.Dump(req) if err := rpc.NewCaller().CallNode(proto.NodeID(rpcEndpoint), rpcName, req, resp); err != nil { // send request failed - log.Infof("call rpc failed: %v\n", err) + ConsoleLog.Infof("call rpc failed: %v\n", err) return } // print the response - log.Info("got response") + ConsoleLog.Info("got response") spewCfg.Dump(resp) - if rpcName == route.MCCAddTx.String() && rpcTxWaitConfirm { - log.Info("waiting for transaction confirmation...") + if rpcName == route.MCCAddTx.String() && waitTxConfirmation { + ConsoleLog.Info("waiting for transaction confirmation...") var ( err error ticker = time.NewTicker(1 * time.Second) @@ -159,7 +170,9 @@ func runRPC() { route.MCCQueryTxState.String(), req, resp, ); err != nil { - log.Fatalf("query transaction state failed: %v", err) + ConsoleLog.WithError(err).Error("query transaction state failed") + SetExitStatus(1) + return } switch resp.State { case pi.TransactionStatePending: @@ -171,10 +184,14 @@ func runRPC() { return case pi.TransactionStateExpired, pi.TransactionStateNotFound: fmt.Print("✘\n") - log.Fatalf("bad transaction state: %s", resp.State) + ConsoleLog.Errorf("bad transaction state: %s", resp.State) + SetExitStatus(1) + return default: fmt.Print("✘\n") - log.Fatal("unknown transaction state") + ConsoleLog.Error("unknown transaction state") + SetExitStatus(1) + return } <-ticker.C } @@ -187,7 +204,7 @@ func checkAndSign(req interface{}) (err error) { } if canSignObj, ok := req.(canSign); ok { - log.Info("signing request") + ConsoleLog.Info("signing request") var privKey *asymmetric.PrivateKey if privKey, err = kms.GetLocalPrivateKey(); err != nil { @@ -283,7 +300,7 @@ func fillTxNonce(tx pi.Transaction) (err error) { } rv.SetUint(uint64(nonceResp.Nonce)) - log.Infof("filled tx type %s nonce field %s with nonce %d", + ConsoleLog.Infof("filled tx type %s nonce field %s with nonce %d", tx.GetTransactionType().String(), fieldPath, nonceResp.Nonce) return @@ -297,7 +314,8 @@ func resolveRPCEntities() (req interface{}, resp interface{}) { if len(rpcParts) != 2 { // error rpc name - log.Fatalf("%v is not a valid rpc name\n", rpcName) + ConsoleLog.Errorf("%v is not a valid rpc name\n", rpcName) + SetExitStatus(1) return } @@ -314,7 +332,7 @@ func resolveRPCEntities() (req interface{}, resp interface{}) { if method.Name == rpcParts[1] { // name matched if mtype.PkgPath() != "" || mtype.NumIn() != 3 || mtype.NumOut() != 1 { - log.Infof("%v is not a valid rpc endpoint method\n", rpcName) + ConsoleLog.Infof("%v is not a valid rpc endpoint method\n", rpcName) return } @@ -335,6 +353,6 @@ func resolveRPCEntities() (req interface{}, resp interface{}) { } // not found - log.Infof("rpc method %v not found\n", rpcName) + ConsoleLog.Infof("rpc method %v not found\n", rpcName) return } diff --git a/cmd/cql/internal/balance.go b/cmd/cql/internal/wallet.go similarity index 67% rename from cmd/cql/internal/balance.go rename to cmd/cql/internal/wallet.go index 2d9dbd018..dcada4498 100644 --- a/cmd/cql/internal/balance.go +++ b/cmd/cql/internal/wallet.go @@ -17,9 +17,11 @@ package internal import ( + "fmt" "strings" "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/crypto" "github.com/CovenantSQL/CovenantSQL/types" ) @@ -27,31 +29,54 @@ var ( tokenName string // get specific token's balance of current account ) -// CmdBalance is cql balance command entity. -var CmdBalance = &Command{ - UsageLine: "cql balance [-config file] [-token token_name]", - Short: "get the balance of current account", +// CmdWallet is cql wallet command entity. +var CmdWallet = &Command{ + UsageLine: "cql wallet [-config file] [-balance token_name]", + Short: "get the wallet address and the balance of current account", Long: ` -Balance command can get CovenantSQL token balance of current account +Wallet command can get CovenantSQL wallet address and the token balance of current account e.g. - cql balance + cql wallet - cql balance -token Particle + cql wallet -balance Particle + cql wallet -balance all `, } func init() { - CmdBalance.Run = runBalance + CmdWallet.Run = runWallet - addCommonFlags(CmdBalance) - CmdBalance.Flag.StringVar(&tokenName, "token", "", "Get specific token's balance of current account, e.g. Particle, Wave, and etc.") + addCommonFlags(CmdWallet) + CmdWallet.Flag.StringVar(&tokenName, "balance", "", "Get specific token's balance of current account, e.g. Particle, Wave, and etc.") } -func runBalance(cmd *Command, args []string) { +func walletGen() { + //TODO if config has wallet, print and return + + publicKey := getPublic() + + keyHash, err := crypto.PubKeyHash(publicKey) + if err != nil { + ConsoleLog.WithError(err).Error("unexpected error") + SetExitStatus(1) + return + } + + fmt.Printf("wallet address: %s\n", keyHash.String()) + + //TODO store in config.yaml +} + +func runWallet(cmd *Command, args []string) { configInit() var err error if tokenName == "" { + walletGen() + return + } + + if tokenName == "all" { var stableCoinBalance, covenantCoinBalance uint64 if stableCoinBalance, err = client.GetTokenBalance(types.Particle); err != nil { diff --git a/cmd/cql/main.go b/cmd/cql/main.go index 8809408ab..2ea524e2e 100644 --- a/cmd/cql/main.go +++ b/cmd/cql/main.go @@ -32,15 +32,18 @@ var ( func init() { internal.CqlCommands = []*internal.Command{ + internal.CmdGenerate, internal.CmdConsole, internal.CmdCreate, internal.CmdDrop, - internal.CmdBalance, + internal.CmdWallet, internal.CmdTransfer, internal.CmdGrant, internal.CmdMirror, internal.CmdExplorer, internal.CmdAdapter, + internal.CmdIDMiner, + internal.CmdRPC, internal.CmdVersion, internal.CmdHelp, } diff --git a/crypto/kms/privatekeystore.go b/crypto/kms/privatekeystore.go index 65dacb9db..207f49228 100644 --- a/crypto/kms/privatekeystore.go +++ b/crypto/kms/privatekeystore.go @@ -161,7 +161,7 @@ func InitLocalKeyPair(privateKeyPath string, masterKey []byte) (err error) { return } } else { - log.WithError(err).Error("unexpected error while loading private key") + log.WithField("path", privateKeyPath).WithError(err).Error("unexpected error while loading private key") return } } diff --git a/test/compatibility/specific_old.sh b/test/compatibility/specific_old.sh index e9d7034ce..1c8c2245f 100755 --- a/test/compatibility/specific_old.sh +++ b/test/compatibility/specific_old.sh @@ -67,7 +67,7 @@ if [[ $CLIENTBIN =~ "v0.4.0" ]]; then ${CLIENTBIN} -config ${PROJECT_DIR}/test/integration/node_c/v0.4.0.yaml -dsn ${dsn} \ -command 'show tables;' | tee result.log else - ${CLIENTBIN} balance -config node_c/config.yaml -no-password + ${CLIENTBIN} wallet -config node_c/config.yaml -balance all -no-password ${CLIENTBIN} create -config node_c/config.yaml -wait-tx-confirm -no-password '{"node":2}' | tee dsn.txt diff --git a/test/testnet_client/run.sh b/test/testnet_client/run.sh index 41b7cadf8..cdd76b3c3 100755 --- a/test/testnet_client/run.sh +++ b/test/testnet_client/run.sh @@ -13,17 +13,17 @@ echo ${PROJECT_DIR} cd ${TEST_WD} -echo -ne "y\n" | ${BIN}/cql-utils -tool confgen -skip-master-key +echo -ne "y\n" | ${BIN}/cql generate -no-password config #get wallet addr -${BIN}/cql-utils -tool addrgen -skip-master-key | tee wallet.txt +${BIN}/cql wallet -no-password | tee wallet.txt wallet=$(awk '{print $3}' wallet.txt) #transfer some coin to above address ${BIN}/cql transfer -config ${PROJECT_DIR}/conf/testnet/config.yaml -wait-tx-confirm -no-password \ '{"addr":"'${wallet}'", "amount":"100000000 Particle"}' -${BIN}/cql balance -no-password +${BIN}/cql wallet -no-password -balance all ${BIN}/cql create -wait-tx-confirm -no-password '{"node":2}' | tee dsn.txt