├── .ci-script ├── build-bin-for-linux-deploy.sh ├── build-bin-for-mac-deploy.sh ├── build-push-docker-image.sh ├── clippy_test.sh └── travis-install-libsodium.sh ├── .env ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README-CN.md ├── README.md ├── cita-cli.gif ├── cita-cli ├── Cargo.toml ├── build.rs └── src │ ├── cli.rs │ ├── cli │ ├── abi_command.rs │ ├── amend_command.rs │ ├── contract_command.rs │ ├── key_command.rs │ ├── other_command.rs │ ├── rpc_command.rs │ ├── store_command.rs │ ├── tx_command.rs │ └── util.rs │ ├── interactive.rs │ ├── json_color.rs │ ├── main.rs │ └── printer.rs ├── cita-tool ├── Cargo.toml ├── contract_abi │ ├── Admin.abi │ ├── Authorization.abi │ ├── BatchTx.abi │ ├── ChainManager.abi │ ├── EmergencyBrake.abi │ ├── Group.abi │ ├── GroupManagement.abi │ ├── NodeManager.abi │ ├── Permission.abi │ ├── PermissionManagement.abi │ ├── PriceManager.abi │ ├── QuotaManager.abi │ ├── Role.abi │ ├── RoleManagement.abi │ ├── SysConfig.abi │ └── VersionManager.abi ├── create_protobuf.sh └── src │ ├── abi.rs │ ├── client.rs │ ├── client │ ├── basic.rs │ ├── system_contract.rs │ └── transaction_option.rs │ ├── crypto.rs │ ├── crypto │ ├── cita_ed25519.rs │ ├── cita_secp256k1.rs │ ├── cita_sm2.rs │ └── crypto_trait.rs │ ├── error.rs │ ├── lib.rs │ ├── protos.rs │ ├── protos │ └── blockchain.rs │ └── rpctypes.rs ├── docker ├── README.md └── release │ └── Dockerfile ├── rust-toolchain └── tool-derive ├── Cargo.toml └── src └── lib.rs /.ci-script/build-bin-for-linux-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # First, install musl-gcc, default on /usr/local/musl 3 | wget https://www.musl-libc.org/releases/musl-1.1.19.tar.gz 4 | tar -zxf musl-1.1.19.tar.gz 5 | cd musl-1.1.19/ 6 | ./configure && make && sudo make install 7 | sudo ln -sf /usr/local/musl/bin/musl-gcc /usr/local/bin/musl-gcc 8 | 9 | # Second, add x86_64-unknown-linux-musl toolchain 10 | rustup target add x86_64-unknown-linux-musl 11 | 12 | # Third, build,generate cita-cli 13 | cd ../cita-cli 14 | cargo install --target x86_64-unknown-linux-musl --path . --force 15 | cd ../docker/release 16 | cp $HOME/.cargo/bin/cita-cli ./ 17 | tar -zcf cita-cli-x86_64-musl-tls-"$TRAVIS_TAG".tar.gz cita-cli 18 | -------------------------------------------------------------------------------- /.ci-script/build-bin-for-mac-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd ./cita-cli 3 | cargo install --no-default-features --features openssl --path . --force 4 | cd ../docker/release 5 | tar -zcf cita-cli-x86_64-mac-osx-tls-"$TRAVIS_TAG".tar.gz $HOME/.cargo/bin/cita-cli 6 | -------------------------------------------------------------------------------- /.ci-script/build-push-docker-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd ./docker/release/ || exit 3 | 4 | docker build . -t "$CITA_CLI_REPOSITORY_NAME":"$TRAVIS_TAG" 5 | cat "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 6 | docker push "$CITA_CLI_REPOSITORY_NAME":"$TRAVIS_TAG" 7 | -------------------------------------------------------------------------------- /.ci-script/clippy_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | MESSAGE=$(git log --format=%B -n 1 HEAD |awk -F "[" '{print $2}' |awk -F "]" '{print $1}') 6 | 7 | if [ "$MESSAGE"x = "skip clippy"x ]; then 8 | echo "skip clippy" 9 | else 10 | rustup component add clippy-preview 11 | cargo clippy 12 | fi 13 | -------------------------------------------------------------------------------- /.ci-script/travis-install-libsodium.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # The purpose of this file is to install libsodium in 3 | # the Travis CI environment. Outside this environment, 4 | # you would probably not want to install it like this. 5 | 6 | set -e 7 | 8 | # check if libsodium is already installed 9 | if [ ! -d "$HOME/libsodium-1.0.16/lib" ]; then 10 | wget https://github.com/jedisct1/libsodium/releases/download/1.0.16/libsodium-1.0.16.tar.gz 11 | tar xvfz libsodium-1.0.16.tar.gz 12 | cd libsodium-1.0.16 13 | ./configure --prefix=$HOME/libsodium-1.0.16 14 | make 15 | make install 16 | else 17 | echo 'Using cached directory.' 18 | fi 19 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | JSONRPC_URL=http://127.0.0.1:1337 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /target/ 3 | cita-cli/target/ 4 | cita-tool/target/ 5 | */.idea/ 6 | 7 | **/*.rs.bk 8 | */Cargo.lock 9 | Cargo.lock 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cita-tool/proto"] 2 | path = cita-tool/proto 3 | url = https://github.com/citahub/cita-proto.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | ################ 2 | # CI Workflow: 3 | # stages: 4 | # 1. test for cita-cli 5 | # 2. build bin 6 | # build bin for linux 7 | # build bin for mac 8 | # deploy: release bin to Github Release, trigger by `git push --tags` 9 | # after_deploy: build cita-cli image and push to https://hub.docker.com/ 10 | 11 | ################ 12 | language: rust 13 | rust: 14 | - stable 15 | sudo: required 16 | cache: 17 | timeout: 1024 18 | directories: 19 | - "$TRAVIS_BUILD_DIR" 20 | - "$TRAVIS_BUILD_DIR/docker/release" 21 | stages: 22 | - auto-test-in-ci 23 | - build-bin-for-linux-deploy 24 | - build-bin-for-mac-deploy 25 | jobs: 26 | include: 27 | - stage: automatic-test-in-ci 28 | os: 29 | - linux 30 | dist: bionic 31 | name: automatic test for cita-cli 32 | before_script: 33 | - rustup component add rustfmt-preview 34 | - rustup component add clippy 35 | script: 36 | # For speed up the CI process 37 | # See: https://docs.travis-ci.com/user/build-stages/#Data-persistence-between-stages-and-jobs 38 | - .ci-script/clippy_test.sh 39 | - cargo fmt -- --check && cargo test --all 40 | - cd cita-cli && cargo test --no-default-features --features openssl 41 | 42 | - stage: build-bin-for-linux-deploy 43 | os: 44 | - linux 45 | dist: bionic 46 | name: buid bin for linux deploy 47 | if: tag IS present 48 | script: 49 | - "./.ci-script/build-bin-for-linux-deploy.sh" 50 | 51 | - stage: build-bin-for-mac-deploy 52 | os: 53 | - osx 54 | name: buid bin for mac deploy 55 | if: tag IS present 56 | script: 57 | - "./.ci-script/build-bin-for-mac-deploy.sh" 58 | deploy: 59 | provider: releases 60 | skip_cleanup: true 61 | api_key: 62 | secure: $GITHUB_TOKEN 63 | file: 64 | - "./docker/release/cita-cli-x86_64-musl-tls-${TRAVIS_TAG}.tar.gz" 65 | - "./docker/release/cita-cli-x86_64-mac-osx-tls-${TRAVIS_TAG}.tar.gz" 66 | on: 67 | tags: true 68 | after_deploy: 69 | - "./.ci-script/build-push-docker-image.sh" 70 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cita-cli", "cita-tool", "tool-derive"] 3 | 4 | # [patch.crates-io] 5 | # ethabi = { git = "https://github.com/paritytech/ethabi", rev = "c300678e0fa8b42bc3d54d876f2e85f37b9a4983" } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 漂流 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # cita-cli 2 | 3 | [![Build Status](https://travis-ci.org/citahub/cita-cli.svg?branch=master)](https://travis-ci.org/citahub/cita-cli) 4 | 5 | [English](./README.md) | 简体中文 6 | 7 | CITA CLI 是用于开发中调试 [CITA](https://github.com/citahub/cita) 的命令行工具。 8 | 9 | CITA CLI 与 CITA 的关系,就像 redis-cli 和 redis。 10 | 11 | ## 简介 12 | 13 | [cita-cli](./cita-cli): 命令行工具的实现。 14 | 15 | [cita-tool](./cita-tool): 用户可直接依赖cita-tool编写与CITA交互的程序。 16 | 17 | > 本项目前提是用户需理解什么是智能合约。 18 | 19 | ## 用户手册 20 | 21 | ### 屏幕截图 22 | 23 | ![A asciicast of cita-cli](./cita-cli.gif) 24 | 25 | ### Clone and Build 26 | 27 | 下载编译后的版本 [here](https://github.com/citahub/cita-cli/releases)。 28 | 如果需要最新版本,可编译源码,支持 Secp256k1/Ed25519/Sm2 加密算法: 29 | 30 | ```bash 31 | $ git clone https://github.com/citahub/cita-cli.git 32 | $ rustup update stable 33 | $ cd cita-cli/cita-cli 34 | $ cargo install --path . 35 | ``` 36 | 会安装在 `~/.cargo/bin/cita-cli`。 37 | 38 | 如果需要使用 `openssl` 去支持 https 请求, 确保 build 环境中存在 `openssl` 并按如下命令编译: 39 | 40 | ```bash 41 | $ cd cita-cli/cita-cli 42 | $ cargo install --features openssl --path . 43 | ``` 44 | 45 | > `rustls` 静态编译在 [release](https://github.com/citahub/cita-cli/releases), 46 | > 并默认支持 https 请求。 47 | 48 | #### 编译 Linux 跨平台版本 49 | 50 | - 首先, 安装 `musl-gcc`, 默认在 `/usr/local/musl` 51 | 52 | ```bash 53 | $ wget https://www.musl-libc.org/releases/musl-1.1.19.tar.gz 54 | $ tar -xzvf musl-1.1.19.tar.gz 55 | $ cd musl-1.1.19/ 56 | $ ./configure && make && sudo make install 57 | $ sudo ln -sf /usr/local/musl/bin/musl-gcc /usr/local/bin/musl-gcc 58 | ``` 59 | 60 | - 第二, 添加 `x86_64-unknown-linux-musl` 61 | 62 | ```bash 63 | $ rustup target add x86_64-unknown-linux-musl 64 | ``` 65 | 66 | - 第三, build 67 | 68 | ```bash 69 | $ cargo install --target x86_64-unknown-linux-musl --path . 70 | ``` 71 | 72 | ### 例子 73 | 74 | 如果认为从命令行指定 URL 太复杂,可以直接编辑env文件,然后 cli 将获得相应的环境变量并自动执行。 75 | 76 | #### 交互模式(推荐) 77 | 78 | ```bash 79 | $ cita-cli 80 | [ url ]: http://121.196.200.225:1337 81 | [ pwd ]: /home/luoc/Rust-work/cita-cli 82 | [ color ]: true 83 | [ debug ]: true 84 | [ json ]: true 85 | [ encryption ]: secp256k1 86 | [ completion_style ]: List 87 | [ edit_style ]: Emacs 88 | [ save_private ]: false 89 | cita> switch --url http://121.196.200.225:1337 90 | cita> rpc blockNumber 91 | { 92 | "jsonrpc": "2.0", 93 | "result": "0x5fbb2", 94 | "id": 1 95 | } 96 | cita> key create 97 | { 98 | "address": "0x1cd05e93e7501c125f14a4859c854fa6d0e63ad6", 99 | "private": "0xfed11b78f963f7cf3de6fc43087900b13e449055e8a9bae1e9dc369412cdddca", 100 | "public": "0xc10a38330fe144062d4a67e2de6f7eed5acf30da9dfd0fb0ecb86d05643afcc4a1b3b34b07731da088c2f564807049ba6632cb94dbcae81d1984ba248d5e5d1e" 101 | } 102 | cita> info 103 | [ url ]: http://121.196.200.225:1337 104 | [ pwd ]: /home/luoc/Rust-work/cita-cli 105 | [ color ]: true 106 | [ debug ]: true 107 | [ json ]: true 108 | [ encryption ]: secp256k1 109 | [ completion_style ]: List 110 | [ edit_style ]: Emacs 111 | [ save_private ]: false 112 | cita> ethabi encode params --param uint256 16 113 | 0000000000000000000000000000000000000000000000000000000000000010 114 | cita> ethabi encode function --file ../HelloWorld.abi --name update --param 16 115 | 82ab890a0000000000000000000000000000000000000000000000000000000000000010 116 | cita> ethabi encode params --param address 08d1a8bbec3dbc2e4fa930dfb6886732f3a72aeb --param uint256 16 117 | "00000000000000000000000008d1a8bbec3dbc2e4fa930dfb6886732f3a72aeb0000000000000000000000000000000000000000000000000000000000000010" 118 | cita> exit 119 | ``` 120 | 121 | #### 命令行模式 122 | 123 | > Tips: 添加 `source <(cita-cli completions bash)` 在你的 `.bashrc` 用来提供命令实现。 124 | 125 | - 获取链高度 126 | ```bash 127 | $ cita-cli rpc blockNumber --url http://121.196.200.225:1337 128 | { 129 | "jsonrpc": "2.0", 130 | "result": "0x1bc7f", 131 | "id": 1 132 | } 133 | ``` 134 | 135 | - 发送交易 136 | ```bash 137 | $ cita-cli rpc sendRawTransaction \ 138 | --private-key "0x352416e1c910e413768c51390dfd791b414212b7b4fe6b1a18f58007fa894214" \ 139 | --code "0x606060405234156100105760006000fd5b610015565b60e0806100236000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604b5780636d4ce63c14606c576045565b60006000fd5b341560565760006000fd5b606a60048080359060200190919050506093565b005b341560775760006000fd5b607d60a3565b6040518082815260200191505060405180910390f35b8060006000508190909055505b50565b6000600060005054905060b1565b905600a165627a7a72305820942223976c6dd48a3aa1d4749f45ad270915cfacd9c0bf3583c018d4c86f9da20029" \ 140 | --height 111146 \ 141 | --url http://121.196.200.225:1337 142 | { 143 | "jsonrpc": "2.0", 144 | "result": { 145 | "status": "OK", 146 | "hash": "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478" 147 | }, 148 | "id": 2 149 | } 150 | ``` 151 | 152 | - 获取交易回执 153 | ```bash 154 | $ cita-cli rpc getTransactionReceipt \ 155 | --hash "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478" \ 156 | --url http://121.196.200.225:1337 157 | { 158 | "jsonrpc": "2.0", 159 | "result": { 160 | "transactionHash": "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478", 161 | "logs": [], 162 | "blockNumber": "0x1b234", 163 | "transactionIndex": "0x0", 164 | "cumulativeGasUsed": "0xafc8", 165 | "gasUsed": "0xafc8", 166 | "blockHash": "0xca3733ac87fab23dc3c6c9b644631c98a937b369183c44f5743c5179587a3028", 167 | "root": null, 168 | "errorMessage": null, 169 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 170 | "contractAddress": "0xd9ae0a3b3e856bf5d01061d99721cc4b136d7e26" 171 | }, 172 | "id": 1 173 | } 174 | ``` 175 | 176 | - 调用合约函数 177 | ```bash 178 | $ cita-cli rpc sendRawTransaction \ 179 | --private-key "0x352416e1c910e413768c51390dfd791b414212b7b4fe6b1a18f58007fa894214" \ 180 | --address "0x73552bc4e960a1d53013b40074569ea05b950b4d" \ 181 | --code "0x60fe47b10000000000000000000000000000000000000000000000000000000000000001" \ 182 | --url http://121.196.200.225:1337 183 | { 184 | "jsonrpc": "2.0", 185 | "result": { 186 | "status": "OK", 187 | "hash": "0x16001b0498c9b80133278a851859b859ada264d2928fd9b2bf0a1ba716079d23" 188 | }, 189 | "id": 2 190 | } 191 | ``` 192 | 193 | - 获取调用结果 194 | ```bash 195 | $ cita-cli rpc call \ 196 | --to 0xd9ae0a3b3e856bf5d01061d99721cc4b136d7e26 \ 197 | --data 0x6d4ce63c \ 198 | --height latest \ 199 | --url http://121.196.200.225:1337 200 | { 201 | "jsonrpc": "2.0", 202 | "result": "0x0000000000000000000000000000000000000000000000000000000000000001", 203 | "id": 1 204 | } 205 | ``` 206 | 207 | - 创建新的公私钥对 208 | ```bash 209 | $ cita-cli key create 210 | { 211 | "address": "0x53ca05180d61bdc1c57b9c819c7545a87b1f3a1d", 212 | "private": "0x49b7b71ce0120d727db74dde8cf7bec89626b5ff2f5c7522f4b8d4ffc878f2b7", 213 | "public": "0xab7d29be188005a54d479a9971fba9faa7f28d637c83166f95de52f7f664b88ac9a3e7b570b462ec66702aac381da84021a52883f18ab1944df08f58db677982" 214 | } 215 | ``` 216 | 217 | - 基于私钥生成公钥和地址 218 | ```bash 219 | $ cita-cli key from-private --private-key 0x993ef0853d7bf1f4c2977457b50ea6b5f8bc2fd829e3ca3e19f6081ddabb07e9 220 | { 221 | "address": "0x9dcd6b234e2772c5451fd4ccf7582f4283140697", 222 | "private": "0x993ef0853d7bf1f4c2977457b50ea6b5f8bc2fd829e3ca3e19f6081ddabb07e9", 223 | "public": "0xa3cadf91b0ad021eb05eaa1fc2bb66109b3d004808c5cc2a1fb251a881aa12615394bde17dfaea4fb84372344d28a1bd2c4a9b4ab3f5d34ae524e2431ce494b6" 224 | } 225 | ``` 226 | 227 | - 生成ABI 228 | ```bash 229 | $ cita-cli ethabi encode params --param uint256 16 230 | 0000000000000000000000000000000000000000000000000000000000000010 231 | $ cita-cli ethabi encode function --file ../HelloWorld.abi --name update --param 16 232 | 82ab890a0000000000000000000000000000000000000000000000000000000000000010 233 | ``` 234 | 235 | ## 贡献 236 | 237 | 请提交在 [https://github.com/citahub/cita-cli](https://github.com/citahub/cita-cli)。 238 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cita-cli 2 | 3 | [![Build Status](https://travis-ci.org/citahub/cita-cli.svg?branch=master)](https://travis-ci.org/citahub/cita-cli) 4 | 5 | English | [简体中文](./README-CN.md) 6 | 7 | An easy-to-use [CITA](https://github.com/citahub/cita) command line tool. 8 | 9 | Just like the relationship between redis-cli and redis. 10 | 11 | ## Overview 12 | 13 | [cita-cli](./cita-cli): a binary project, command line tool. 14 | 15 | [cita-tool](./cita-tool): a crate to support cita-cli, of course, can also be used for secondary development, which contains all the methods needed. 16 | 17 | > This project assumes that the user understands what the smart contract is. 18 | 19 | ## Usage 20 | 21 | ### Screencast 22 | 23 | ![A asciicast of cita-cli](./cita-cli.gif) 24 | 25 | ### Clone and Build 26 | 27 | You can download the compiled version [here](https://github.com/citahub/cita-cli/releases). 28 | If you need the latest version, compile from the source code, which supports Secp256k1/Ed25519/Sm2 algorithms: 29 | 30 | ```bash 31 | $ git clone https://github.com/citahub/cita-cli.git 32 | $ rustup update stable 33 | $ cd cita-cli/cita-cli 34 | $ cargo install --path . 35 | ``` 36 | It will install to `~/.cargo/bin/cita-cli`. 37 | 38 | If you want to use `openssl` to support https requests, make sure that `openssl` exists in the build environment and 39 | compile with the following command: 40 | 41 | ```bash 42 | $ cd cita-cli/cita-cli 43 | $ cargo install --no-default-features --features openssl --path . 44 | ``` 45 | 46 | > `rustls` is statically compiled in [release](https://github.com/citahub/cita-cli/releases), 47 | > and https requests is supported by default. 48 | 49 | #### Compile the Linux cross-platform version 50 | 51 | - First, install `musl-gcc`, default on `/usr/local/musl` 52 | 53 | ```bash 54 | $ wget https://www.musl-libc.org/releases/musl-1.1.19.tar.gz 55 | $ tar -xzvf musl-1.1.19.tar.gz 56 | $ cd musl-1.1.19/ 57 | $ ./configure && make && sudo make install 58 | $ sudo ln -sf /usr/local/musl/bin/musl-gcc /usr/local/bin/musl-gcc 59 | ``` 60 | 61 | - Second, add `x86_64-unknown-linux-musl` toolchain 62 | 63 | ```bash 64 | $ rustup target add x86_64-unknown-linux-musl 65 | ``` 66 | 67 | - Third, build 68 | 69 | ```bash 70 | $ cargo install --target x86_64-unknown-linux-musl --path . 71 | ``` 72 | 73 | ### Examples 74 | 75 | If you think that specifying the URL from the command line is too complex, you can edit the env file directly, 76 | then the cli will get the corresponding environment variable and use it automatically. 77 | 78 | #### Interactive mode(recommend) 79 | 80 | ```bash 81 | $ cita-cli 82 | [ url ]: http://121.196.200.225:1337 83 | [ pwd ]: /home/luoc/Rust-work/cita-cli 84 | [ color ]: true 85 | [ debug ]: true 86 | [ json ]: true 87 | [ encryption ]: secp256k1 88 | [ completion_style ]: List 89 | [ edit_style ]: Emacs 90 | [ save_private ]: false 91 | cita> switch --url http://121.196.200.225:1337 92 | cita> rpc blockNumber 93 | { 94 | "jsonrpc": "2.0", 95 | "result": "0x5fbb2", 96 | "id": 1 97 | } 98 | cita> key create 99 | { 100 | "address": "0x1cd05e93e7501c125f14a4859c854fa6d0e63ad6", 101 | "private": "0xfed11b78f963f7cf3de6fc43087900b13e449055e8a9bae1e9dc369412cdddca", 102 | "public": "0xc10a38330fe144062d4a67e2de6f7eed5acf30da9dfd0fb0ecb86d05643afcc4a1b3b34b07731da088c2f564807049ba6632cb94dbcae81d1984ba248d5e5d1e" 103 | } 104 | cita> info 105 | [ url ]: http://121.196.200.225:1337 106 | [ pwd ]: /home/luoc/Rust-work/cita-cli 107 | [ color ]: true 108 | [ debug ]: true 109 | [ json ]: true 110 | [ encryption ]: secp256k1 111 | [ completion_style ]: List 112 | [ edit_style ]: Emacs 113 | [ save_private ]: false 114 | cita> ethabi encode params --param uint256 16 115 | 0000000000000000000000000000000000000000000000000000000000000010 116 | cita> ethabi encode function --file ../HelloWorld.abi --name update --param 16 117 | 82ab890a0000000000000000000000000000000000000000000000000000000000000010 118 | cita> ethabi encode params --param address 08d1a8bbec3dbc2e4fa930dfb6886732f3a72aeb --param uint256 16 119 | "00000000000000000000000008d1a8bbec3dbc2e4fa930dfb6886732f3a72aeb0000000000000000000000000000000000000000000000000000000000000010" 120 | cita> exit 121 | ``` 122 | 123 | #### Command line mode 124 | 125 | > Tips: Add `source <(cita-cli completions bash)` to your `.bashrc` to provide command completion. 126 | 127 | - Get chain height 128 | ```bash 129 | $ cita-cli rpc blockNumber --url http://121.196.200.225:1337 130 | { 131 | "jsonrpc": "2.0", 132 | "result": "0x1bc7f", 133 | "id": 1 134 | } 135 | ``` 136 | 137 | - Send transaction 138 | ```bash 139 | $ cita-cli rpc sendRawTransaction \ 140 | --private-key "0x352416e1c910e413768c51390dfd791b414212b7b4fe6b1a18f58007fa894214" \ 141 | --code "0x606060405234156100105760006000fd5b610015565b60e0806100236000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604b5780636d4ce63c14606c576045565b60006000fd5b341560565760006000fd5b606a60048080359060200190919050506093565b005b341560775760006000fd5b607d60a3565b6040518082815260200191505060405180910390f35b8060006000508190909055505b50565b6000600060005054905060b1565b905600a165627a7a72305820942223976c6dd48a3aa1d4749f45ad270915cfacd9c0bf3583c018d4c86f9da20029" \ 142 | --height 111146 \ 143 | --url http://121.196.200.225:1337 144 | { 145 | "jsonrpc": "2.0", 146 | "result": { 147 | "status": "OK", 148 | "hash": "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478" 149 | }, 150 | "id": 2 151 | } 152 | ``` 153 | 154 | - Get transaction receipt 155 | ```bash 156 | $ cita-cli rpc getTransactionReceipt \ 157 | --hash "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478" \ 158 | --url http://121.196.200.225:1337 159 | { 160 | "jsonrpc": "2.0", 161 | "result": { 162 | "transactionHash": "0x16251c374ee87eae41cbd9203eea481b861738a19c19df9d3c6603b9fbe84478", 163 | "logs": [], 164 | "blockNumber": "0x1b234", 165 | "transactionIndex": "0x0", 166 | "cumulativeGasUsed": "0xafc8", 167 | "gasUsed": "0xafc8", 168 | "blockHash": "0xca3733ac87fab23dc3c6c9b644631c98a937b369183c44f5743c5179587a3028", 169 | "root": null, 170 | "errorMessage": null, 171 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 172 | "contractAddress": "0xd9ae0a3b3e856bf5d01061d99721cc4b136d7e26" 173 | }, 174 | "id": 1 175 | } 176 | ``` 177 | 178 | - Call contract function 179 | ```bash 180 | $ cita-cli rpc sendRawTransaction \ 181 | --private-key "0x352416e1c910e413768c51390dfd791b414212b7b4fe6b1a18f58007fa894214" \ 182 | --address "0x73552bc4e960a1d53013b40074569ea05b950b4d" \ 183 | --code "0x60fe47b10000000000000000000000000000000000000000000000000000000000000001" \ 184 | --url http://121.196.200.225:1337 185 | { 186 | "jsonrpc": "2.0", 187 | "result": { 188 | "status": "OK", 189 | "hash": "0x16001b0498c9b80133278a851859b859ada264d2928fd9b2bf0a1ba716079d23" 190 | }, 191 | "id": 2 192 | } 193 | ``` 194 | 195 | - Get call result 196 | ```bash 197 | $ cita-cli rpc call \ 198 | --to 0xd9ae0a3b3e856bf5d01061d99721cc4b136d7e26 \ 199 | --data 0x6d4ce63c \ 200 | --height latest \ 201 | --url http://121.196.200.225:1337 202 | { 203 | "jsonrpc": "2.0", 204 | "result": "0x0000000000000000000000000000000000000000000000000000000000000001", 205 | "id": 1 206 | } 207 | ``` 208 | 209 | - Create new key pair 210 | ```bash 211 | $ cita-cli key create 212 | { 213 | "address": "0x53ca05180d61bdc1c57b9c819c7545a87b1f3a1d", 214 | "private": "0x49b7b71ce0120d727db74dde8cf7bec89626b5ff2f5c7522f4b8d4ffc878f2b7", 215 | "public": "0xab7d29be188005a54d479a9971fba9faa7f28d637c83166f95de52f7f664b88ac9a3e7b570b462ec66702aac381da84021a52883f18ab1944df08f58db677982" 216 | } 217 | ``` 218 | 219 | - Generate public keys and addresses based on private keys 220 | ```bash 221 | $ cita-cli key from-private --private-key 0x993ef0853d7bf1f4c2977457b50ea6b5f8bc2fd829e3ca3e19f6081ddabb07e9 222 | { 223 | "address": "0x9dcd6b234e2772c5451fd4ccf7582f4283140697", 224 | "private": "0x993ef0853d7bf1f4c2977457b50ea6b5f8bc2fd829e3ca3e19f6081ddabb07e9", 225 | "public": "0xa3cadf91b0ad021eb05eaa1fc2bb66109b3d004808c5cc2a1fb251a881aa12615394bde17dfaea4fb84372344d28a1bd2c4a9b4ab3f5d34ae524e2431ce494b6" 226 | } 227 | ``` 228 | 229 | - ABI generate 230 | ```bash 231 | $ cita-cli ethabi encode params --param uint256 16 232 | 0000000000000000000000000000000000000000000000000000000000000010 233 | $ cita-cli ethabi encode function --file ../HelloWorld.abi --name update --param 16 234 | 82ab890a0000000000000000000000000000000000000000000000000000000000000010 235 | ``` 236 | 237 | ## Contribute 238 | 239 | Please submit to [https://github.com/citahub/cita-cli](https://github.com/citahub/cita-cli). 240 | -------------------------------------------------------------------------------- /cita-cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/citahub/cita-cli/2bc9a5b46817ce7b2f6d03128a8ebc5b73e1ee8c/cita-cli.gif -------------------------------------------------------------------------------- /cita-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cita-cli" 3 | version = "20.2.2" 4 | authors = ["piaoliu <441594700@qq.com>", "Qian Linfeng "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | dotenv = "^0.13.0" 10 | clap = "^2.31.2" 11 | ansi_term = "^0.11.0" 12 | colored = "^1.6.0" 13 | atty = "^0.2.10" 14 | serde = "^1.0.66" 15 | serde_json = "^1.0.17" 16 | shell-words = "^0.1.0" 17 | rustyline = "^5.0.2" 18 | cita-tool = { path = "../cita-tool", default-features = false } 19 | dirs = "^2.0.0" 20 | regex = "^1.0.4" 21 | ## lazy_static = "^1.0" 22 | 23 | [features] 24 | default = ["rustls"] 25 | openssl = ["cita-tool/openssl"] 26 | rustls = ["cita-tool/rustls"] 27 | -------------------------------------------------------------------------------- /cita-cli/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::Path; 5 | 6 | fn main() { 7 | let out_dir = env::var("OUT_DIR").unwrap(); 8 | let dest_path = Path::new(&out_dir).join("build_info.rs"); 9 | let mut f = File::create(&dest_path).unwrap(); 10 | 11 | let code = format!( 12 | " 13 | pub fn get_commit_id() -> &'static str {{ 14 | {:?} 15 | }} 16 | ", 17 | format!( 18 | "{} {}", 19 | get_commit_describe().unwrap_or_default(), 20 | get_commit_date().unwrap_or_default() 21 | ) 22 | ); 23 | 24 | f.write_all(code.as_bytes()).unwrap(); 25 | } 26 | 27 | pub fn get_commit_describe() -> Option { 28 | std::process::Command::new("git") 29 | .args(&["describe", "--dirty", "--tags"]) 30 | .output() 31 | .ok() 32 | .and_then(|r| { 33 | String::from_utf8(r.stdout).ok().map(|s| { 34 | s.trim() 35 | .splitn(2, '-') 36 | .collect::>() 37 | .pop() 38 | .unwrap_or_default() 39 | .to_string() 40 | }) 41 | }) 42 | } 43 | 44 | pub fn get_commit_date() -> Option { 45 | std::process::Command::new("git") 46 | .env("TZ", "UTC") 47 | .args(&["log", "-1", "--date=short-local", "--pretty=format:%cd"]) 48 | .output() 49 | .ok() 50 | .and_then(|r| { 51 | String::from_utf8(r.stdout) 52 | .ok() 53 | .map(|s| s.trim().to_string()) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /cita-cli/src/cli.rs: -------------------------------------------------------------------------------- 1 | mod abi_command; 2 | mod amend_command; 3 | mod contract_command; 4 | mod key_command; 5 | mod other_command; 6 | mod rpc_command; 7 | mod store_command; 8 | mod tx_command; 9 | mod util; 10 | 11 | pub(crate) use self::util::{ 12 | encryption, get_url, h256_validator, is_hex, key_validator, parse_address, parse_height, 13 | parse_privkey, parse_u256, parse_u32, parse_u64, search_app, 14 | }; 15 | 16 | pub use self::abi_command::{abi_command, abi_processor}; 17 | pub use self::amend_command::{amend_command, amend_processor}; 18 | pub use self::contract_command::{contract_command, contract_processor}; 19 | pub use self::key_command::{key_command, key_processor}; 20 | pub use self::other_command::{ 21 | benchmark_command, benchmark_processor, completion_command, completion_processor, 22 | search_command, search_processor, string_include, transfer_command, transfer_processor, 23 | }; 24 | pub use self::rpc_command::{rpc_command, rpc_processor}; 25 | pub use self::store_command::{store_command, store_processor}; 26 | pub use self::tx_command::{tx_command, tx_processor}; 27 | 28 | use cita_tool::parse_url; 29 | use clap::{crate_version, App, AppSettings, Arg, SubCommand}; 30 | 31 | /// Generate cli 32 | pub fn build_cli(version: &str) -> App { 33 | let arg_url = Arg::with_name("url") 34 | .long("url") 35 | .takes_value(true) 36 | .validator(|url| parse_url(url.as_ref()).map(|_| ())) 37 | .global(true) 38 | .help("JSONRPC server URL (dotenv: JSONRPC_URL)"); 39 | App::new("cita-cli") 40 | .version(version) 41 | .global_setting(AppSettings::ColoredHelp) 42 | .global_setting(AppSettings::DeriveDisplayOrder) 43 | .subcommand(rpc_command().arg(arg_url.clone())) 44 | .subcommand(contract_command().arg(arg_url.clone())) 45 | .subcommand(key_command()) 46 | .subcommand(abi_command()) 47 | .subcommand(transfer_command().arg(arg_url.clone())) 48 | .subcommand(store_command().arg(arg_url.clone())) 49 | .subcommand(amend_command().arg(arg_url.clone())) 50 | .subcommand(search_command()) 51 | .subcommand(tx_command().arg(arg_url.clone())) 52 | .subcommand(benchmark_command().arg(arg_url.clone())) 53 | .subcommand(completion_command()) 54 | .arg( 55 | Arg::with_name("algorithm") 56 | .long("algorithm") 57 | .global(true) 58 | .takes_value(true) 59 | .possible_values(&["secp256k1", "ed25519", "sm2"]) 60 | .help("Select the encryption algorithm you want, the default is secp256k1"), 61 | ) 62 | .arg( 63 | Arg::with_name("no-color") 64 | .long("no-color") 65 | .global(true) 66 | .help("Do not highlight(color) output json"), 67 | ) 68 | .arg( 69 | Arg::with_name("debug") 70 | .long("debug") 71 | .global(true) 72 | .help("Display request parameters"), 73 | ) 74 | } 75 | 76 | /// Interactive parser 77 | pub fn build_interactive() -> App<'static, 'static> { 78 | App::new("interactive") 79 | .version(crate_version!()) 80 | .setting(AppSettings::NoBinaryName) 81 | .global_setting(AppSettings::ColoredHelp) 82 | .global_setting(AppSettings::DeriveDisplayOrder) 83 | .global_setting(AppSettings::DisableVersion) 84 | .subcommand( 85 | SubCommand::with_name("switch") 86 | .about("Switch environment variables, such as url/algorithm") 87 | .arg( 88 | Arg::with_name("url") 89 | .long("url") 90 | .validator(|url| parse_url(url.as_ref()).map(|_| ())) 91 | .takes_value(true) 92 | .help("Switch url"), 93 | ) 94 | .arg( 95 | Arg::with_name("color") 96 | .long("color") 97 | .help("Switching color for rpc interface"), 98 | ) 99 | .arg( 100 | Arg::with_name("algorithm") 101 | .long("algorithm") 102 | .takes_value(true) 103 | .possible_values(&["secp256k1", "ed25519", "sm2"]) 104 | .help("Select the encryption algorithm you want, the default is secp256k1"), 105 | ) 106 | .arg( 107 | Arg::with_name("debug") 108 | .long("debug") 109 | .help("Switching debug mode"), 110 | ) 111 | .arg( 112 | Arg::with_name("json") 113 | .long("json") 114 | .help("Switching json format"), 115 | ) 116 | .arg( 117 | Arg::with_name("completion_style") 118 | .long("completion_style") 119 | .help("Switching completion style"), 120 | ) 121 | .arg( 122 | Arg::with_name("edit_style") 123 | .long("edit_style") 124 | .help("Switching edit style"), 125 | ) 126 | .arg( 127 | Arg::with_name("save_private") 128 | .long("save_private") 129 | .help("Switching whether save private key"), 130 | ), 131 | ) 132 | .subcommand(search_command()) 133 | .subcommand(SubCommand::with_name("info").about("Display global variables")) 134 | .subcommand(rpc_command()) 135 | .subcommand(key_command()) 136 | .subcommand(abi_command()) 137 | .subcommand(contract_command()) 138 | .subcommand(transfer_command()) 139 | .subcommand(store_command()) 140 | .subcommand(amend_command()) 141 | .subcommand(tx_command()) 142 | .subcommand(benchmark_command()) 143 | .subcommand( 144 | SubCommand::with_name("exit") 145 | .visible_alias("quit") 146 | .about("Exit the interactive interface"), 147 | ) 148 | .subcommand( 149 | SubCommand::with_name("set") 150 | .about("Set temporary variables") 151 | .arg( 152 | Arg::with_name("key") 153 | .required(true) 154 | .index(1) 155 | .help("The name of variable"), 156 | ) 157 | .arg( 158 | Arg::with_name("value") 159 | .required(true) 160 | .index(2) 161 | .help("Variable value"), 162 | ), 163 | ) 164 | .subcommand( 165 | SubCommand::with_name("get") 166 | .about("Get variable value") 167 | .arg(Arg::with_name("key").index(1).help("The name of variable")), 168 | ) 169 | } 170 | -------------------------------------------------------------------------------- /cita-cli/src/cli/abi_command.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg, ArgMatches, SubCommand}; 2 | use serde_json::{self, Value}; 3 | 4 | use crate::interactive::GlobalConfig; 5 | use crate::printer::Printer; 6 | use cita_tool::{decode_input, decode_logs, decode_params, encode_input, encode_params, remove_0x}; 7 | 8 | /// Ethereum abi sub command 9 | pub fn abi_command() -> App<'static, 'static> { 10 | let param_arg = Arg::with_name("param") 11 | .long("param") 12 | .takes_value(true) 13 | .multiple(true) 14 | .allow_hyphen_values(true) 15 | .number_of_values(2) 16 | .help("Function parameters"); 17 | let no_lenient_flag = Arg::with_name("no-lenient") 18 | .long("no-lenient") 19 | .help("Don't allow short representation of input params"); 20 | let abi_arg = Arg::with_name("abi") 21 | .long("abi") 22 | .takes_value(true) 23 | .required(true) 24 | .conflicts_with("file") 25 | .help("ABI json string"); 26 | let file_arg = Arg::with_name("file") 27 | .long("file") 28 | .takes_value(true) 29 | .help("ABI json file path"); 30 | 31 | App::new("ethabi") 32 | .about("ABI operation, encode parameter, generate code based on abi and parameters") 33 | .subcommand( 34 | SubCommand::with_name("encode") 35 | .subcommand( 36 | SubCommand::with_name("function") 37 | .arg(abi_arg.clone()) 38 | .arg(file_arg.clone()) 39 | .arg( 40 | Arg::with_name("name") 41 | .long("name") 42 | .takes_value(true) 43 | .required(true) 44 | .help("Function name"), 45 | ) 46 | .arg(param_arg.clone().number_of_values(1).value_name("value")) 47 | .arg(no_lenient_flag.clone()), 48 | ) 49 | .subcommand( 50 | SubCommand::with_name("params") 51 | .arg(param_arg.clone().value_names(&["type", "value"])) 52 | .arg(no_lenient_flag.clone()), 53 | ) 54 | .subcommand( 55 | SubCommand::with_name("constructor") 56 | .arg(abi_arg.clone()) 57 | .arg(file_arg.clone()) 58 | .arg( 59 | Arg::with_name("code") 60 | .long("code") 61 | .takes_value(true) 62 | .default_value("") 63 | .help("Contract bin code"), 64 | ) 65 | .arg(no_lenient_flag) 66 | .arg(param_arg.clone().number_of_values(1).value_name("value")), 67 | ), 68 | ) 69 | .subcommand( 70 | SubCommand::with_name("decode") 71 | .subcommand( 72 | SubCommand::with_name("params") 73 | .arg( 74 | Arg::with_name("type") 75 | .long("type") 76 | .takes_value(true) 77 | .multiple(true) 78 | .help("Decode types"), 79 | ) 80 | .arg( 81 | Arg::with_name("data") 82 | .long("data") 83 | .takes_value(true) 84 | .help("Decode data"), 85 | ), 86 | ) 87 | .subcommand( 88 | SubCommand::with_name("function") 89 | .arg(abi_arg.clone()) 90 | .arg(file_arg.clone()) 91 | .arg( 92 | Arg::with_name("name") 93 | .long("name") 94 | .takes_value(true) 95 | .required(true) 96 | .help("Function name"), 97 | ) 98 | .arg( 99 | Arg::with_name("data") 100 | .long("data") 101 | .required(true) 102 | .takes_value(true) 103 | .help("Decode data"), 104 | ), 105 | ) 106 | .subcommand( 107 | SubCommand::with_name("log") 108 | .arg(abi_arg.clone()) 109 | .arg(file_arg.clone()) 110 | .arg( 111 | Arg::with_name("event") 112 | .long("event") 113 | .takes_value(true) 114 | .required(true) 115 | .help("Event name"), 116 | ) 117 | .arg(param_arg.clone().number_of_values(1).value_name("topic")) 118 | .arg( 119 | Arg::with_name("data") 120 | .long("data") 121 | .required(true) 122 | .takes_value(true) 123 | .help("Decode data"), 124 | ), 125 | ), 126 | ) 127 | } 128 | 129 | /// ABI processor 130 | pub fn abi_processor( 131 | sub_matches: &ArgMatches, 132 | printer: &Printer, 133 | config: &GlobalConfig, 134 | ) -> Result<(), String> { 135 | let is_color = !sub_matches.is_present("no-color") && config.color(); 136 | match sub_matches.subcommand() { 137 | ("encode", Some(em)) => match em.subcommand() { 138 | ("function", Some(m)) => { 139 | let file = m.value_of("file"); 140 | let abi = m.value_of("abi"); 141 | let name = m.value_of("name").unwrap(); 142 | let lenient = !m.is_present("no-lenient"); 143 | let values: Vec = match m.values_of("param") { 144 | None => Vec::new(), 145 | Some(param) => param.map(ToOwned::to_owned).collect::>(), 146 | }; 147 | let output = encode_input(file, abi, name, &values, lenient, false) 148 | .map_err(|err| format!("{}", err))?; 149 | printer.println(&Value::String(output), is_color); 150 | } 151 | ("params", Some(m)) => { 152 | let lenient = !m.is_present("no-lenient"); 153 | let mut types: Vec = Vec::new(); 154 | let mut values: Vec = Vec::new(); 155 | let mut param_iter = m 156 | .values_of("param") 157 | .ok_or_else(|| "Please give at least one parameter.".to_string())? 158 | .peekable(); 159 | while param_iter.peek().is_some() { 160 | types.push(param_iter.next().unwrap().to_owned()); 161 | values.push(param_iter.next().unwrap().to_owned()); 162 | } 163 | let output = 164 | encode_params(&types, &values, lenient).map_err(|err| format!("{}", err))?; 165 | printer.println(&Value::String(output), is_color); 166 | } 167 | ("constructor", Some(m)) => { 168 | let file = m.value_of("file"); 169 | let abi = m.value_of("abi"); 170 | let code = m.value_of("code").unwrap(); 171 | let lenient = !m.is_present("no-lenient"); 172 | let values: Vec = match m.values_of("param") { 173 | None => Vec::new(), 174 | Some(param) => param.map(ToOwned::to_owned).collect::>(), 175 | }; 176 | let output = encode_input(file, abi, code, &values, lenient, true) 177 | .map_err(|err| format!("{}", err))?; 178 | printer.println(&Value::String(output), is_color); 179 | } 180 | _ => { 181 | return Err(em.usage().to_owned()); 182 | } 183 | }, 184 | ("decode", Some(em)) => match em.subcommand() { 185 | ("params", Some(m)) => { 186 | let types: Vec = m 187 | .values_of("type") 188 | .ok_or_else(|| "Please give at least one parameter.".to_string())? 189 | .map(ToOwned::to_owned) 190 | .collect(); 191 | let data = remove_0x(m.value_of("data").unwrap()); 192 | let output = decode_params(&types, data) 193 | .map_err(|err| err.to_string())? 194 | .iter() 195 | .map(|value| serde_json::from_str(value).unwrap()) 196 | .collect(); 197 | printer.println(&Value::Array(output), is_color); 198 | } 199 | ("function", Some(m)) => { 200 | let file = m.value_of("file"); 201 | let abi = m.value_of("abi"); 202 | let name = m.value_of("name").unwrap(); 203 | let values = m.value_of("data").unwrap(); 204 | let output = decode_input(file, abi, name, values) 205 | .map_err(|err| format!("{}", err))? 206 | .iter() 207 | .map(|value| serde_json::from_str(value).unwrap()) 208 | .collect(); 209 | printer.println(&Value::Array(output), is_color); 210 | } 211 | ("log", Some(m)) => { 212 | let file = m.value_of("file"); 213 | let abi = m.value_of("abi"); 214 | let event = m.value_of("event").unwrap(); 215 | let topic: Vec = match m.values_of("param") { 216 | None => Vec::new(), 217 | Some(param) => param.map(ToOwned::to_owned).collect::>(), 218 | }; 219 | let data = m.value_of("data").unwrap(); 220 | let output = decode_logs(file, abi, event, &topic, data) 221 | .map_err(|err| format!("{}", err))? 222 | .iter() 223 | .map(|value| serde_json::from_str(value).unwrap()) 224 | .collect(); 225 | printer.println(&Value::Array(output), is_color); 226 | } 227 | _ => { 228 | return Err(em.usage().to_owned()); 229 | } 230 | }, 231 | _ => { 232 | return Err(sub_matches.usage().to_owned()); 233 | } 234 | } 235 | Ok(()) 236 | } 237 | -------------------------------------------------------------------------------- /cita-cli/src/cli/amend_command.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; 2 | 3 | use cita_tool::client::basic::{AmendExt, Client}; 4 | use cita_tool::remove_0x; 5 | 6 | use crate::cli::{ 7 | encryption, get_url, h256_validator, key_validator, parse_address, parse_privkey, parse_u256, 8 | parse_u64, 9 | }; 10 | use crate::interactive::{set_output, GlobalConfig}; 11 | use crate::printer::Printer; 12 | 13 | use std::fs; 14 | use std::io::Read; 15 | 16 | /// Amend(Update) ABI/contract code/H256KV 17 | pub fn amend_command() -> App<'static, 'static> { 18 | let common_args = [ 19 | Arg::with_name("chain-id") 20 | .long("chain-id") 21 | .takes_value(true) 22 | .validator(|chain_id| match chain_id.parse::() { 23 | Ok(_) => Ok(()), 24 | Err(err) => Err(format!("{:?}", err)), 25 | }) 26 | .help("The chain_id of transaction"), 27 | Arg::with_name("admin-private-key") 28 | .long("admin-private-key") 29 | .takes_value(true) 30 | .required(true) 31 | .validator(|privkey| key_validator(privkey.as_ref()).map(|_| ())) 32 | .help("The private key of super admin"), 33 | Arg::with_name("quota") 34 | .long("quota") 35 | .takes_value(true) 36 | .validator(|quota| parse_u64(quota.as_ref()).map(|_| ())) 37 | .help("Transaction quota costs, default is 1_000_000"), 38 | ]; 39 | App::new("amend") 40 | .about("Amend(update) ABI/contract code/H256KV") 41 | .subcommand( 42 | SubCommand::with_name("code") 43 | .about("Amend contract code") 44 | .arg( 45 | Arg::with_name("address") 46 | .long("address") 47 | .validator(|address| parse_address(address.as_str())) 48 | .required(true) 49 | .takes_value(true) 50 | .help("The contract address of the code"), 51 | ) 52 | .arg( 53 | Arg::with_name("content") 54 | .long("content") 55 | .takes_value(true) 56 | .required(true) 57 | .help("The contract code to amend"), 58 | ) 59 | .args(&common_args), 60 | ) 61 | .subcommand( 62 | SubCommand::with_name("abi") 63 | .about("Amend contract ABI data") 64 | .arg( 65 | Arg::with_name("address") 66 | .long("address") 67 | .validator(|address| parse_address(address.as_str())) 68 | .required(true) 69 | .takes_value(true) 70 | .help("The contract address of the ABI"), 71 | ) 72 | .arg( 73 | Arg::with_name("content") 74 | .long("content") 75 | .required(true) 76 | .takes_value(true) 77 | .help("The content of ABI data to amend (json)"), 78 | ) 79 | .arg( 80 | Arg::with_name("path") 81 | .long("path") 82 | .takes_value(true) 83 | .help("The path of ABI json file to amend (.json)"), 84 | ) 85 | .group(ArgGroup::with_name("the-abi").args(&["content", "path"])) 86 | .args(&common_args), 87 | ) 88 | .subcommand( 89 | SubCommand::with_name("set-h256") 90 | .about("Amend H256 Key,Value pair") 91 | .arg( 92 | Arg::with_name("address") 93 | .long("address") 94 | .validator(|address| parse_address(address.as_str())) 95 | .required(true) 96 | .takes_value(true) 97 | .help("The account address"), 98 | ) 99 | .arg( 100 | Arg::with_name("kv") 101 | .long("kv") 102 | .required(true) 103 | .takes_value(true) 104 | .multiple(true) 105 | .number_of_values(2) 106 | .validator(|kv| h256_validator(kv.as_str())) 107 | .help("The key value pair"), 108 | ) 109 | .args(&common_args), 110 | ) 111 | .subcommand( 112 | SubCommand::with_name("balance") 113 | .about("Amend account balance") 114 | .arg( 115 | Arg::with_name("address") 116 | .long("address") 117 | .validator(|address| parse_address(address.as_str())) 118 | .required(true) 119 | .takes_value(true) 120 | .help("The account address"), 121 | ) 122 | .arg( 123 | Arg::with_name("value") 124 | .long("value") 125 | .required(true) 126 | .takes_value(true) 127 | .validator(|value| parse_u256(value.as_ref()).map(|_| ())) 128 | .help("Account balance"), 129 | ) 130 | .args(&common_args), 131 | ) 132 | } 133 | 134 | /// Amend processor 135 | pub fn amend_processor( 136 | sub_matches: &ArgMatches, 137 | printer: &Printer, 138 | config: &mut GlobalConfig, 139 | client: Client, 140 | ) -> Result<(), String> { 141 | let debug = sub_matches.is_present("debug") || config.debug(); 142 | let mut client = client 143 | .set_debug(debug) 144 | .set_uri(get_url(sub_matches, config)); 145 | 146 | let result = match sub_matches.subcommand() { 147 | ("code", Some(m)) => { 148 | let encryption = encryption(m, config); 149 | if let Some(private_key) = m.value_of("admin-private-key") { 150 | client.set_private_key(&parse_privkey(private_key, encryption)?); 151 | } 152 | let address = m.value_of("address").unwrap(); 153 | let content = m.value_of("content").unwrap(); 154 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 155 | client.amend_code(address, content, quota) 156 | } 157 | ("abi", Some(m)) => { 158 | let encryption = encryption(m, config); 159 | if let Some(private_key) = m.value_of("admin-private-key") { 160 | client.set_private_key(&parse_privkey(private_key, encryption)?); 161 | } 162 | let content = match m.value_of("content") { 163 | Some(content) => content.to_owned(), 164 | None => { 165 | let mut abi_content = String::new(); 166 | let path = m.value_of("path").unwrap(); 167 | let mut file = fs::File::open(path).map_err(|err| format!("{}", err))?; 168 | file.read_to_string(&mut abi_content) 169 | .map_err(|err| format!("{}", err))?; 170 | abi_content 171 | } 172 | }; 173 | let address = m.value_of("address").unwrap(); 174 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 175 | client.amend_abi(address, content, quota) 176 | } 177 | ("set-h256", Some(m)) => { 178 | let encryption = encryption(m, config); 179 | if let Some(private_key) = m.value_of("admin-private-key") { 180 | client.set_private_key(&parse_privkey(private_key, encryption)?); 181 | } 182 | let address = m.value_of("address").unwrap(); 183 | let h256_kv = m 184 | .values_of("kv") 185 | .unwrap() 186 | .map(|s| remove_0x(s)) 187 | .collect::>() 188 | .join(""); 189 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 190 | client.amend_h256kv(address, &h256_kv, quota) 191 | } 192 | ("balance", Some(m)) => { 193 | let encryption = encryption(m, config); 194 | if let Some(private_key) = m.value_of("admin-private-key") { 195 | client.set_private_key(&parse_privkey(private_key, encryption)?); 196 | } 197 | let address = m.value_of("address").unwrap(); 198 | let balance = m 199 | .value_of("value") 200 | .map(|value| parse_u256(value).unwrap()) 201 | .unwrap(); 202 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 203 | client.amend_balance(address, balance, quota) 204 | } 205 | _ => { 206 | return Err(sub_matches.usage().to_owned()); 207 | } 208 | }; 209 | let resp = result.map_err(|err| format!("{}", err))?; 210 | let is_color = !sub_matches.is_present("no-color") && config.color(); 211 | printer.println(&resp, is_color); 212 | set_output(&resp, config); 213 | Ok(()) 214 | } 215 | -------------------------------------------------------------------------------- /cita-cli/src/cli/key_command.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::Colour::Yellow; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | 4 | use cita_tool::{ 5 | decode, pubkey_to_address, remove_0x, Hashable, KeyPair, LowerHex, Message, PubKey, Signature, 6 | }; 7 | 8 | use crate::cli::{encryption, h256_validator, is_hex, key_validator}; 9 | use crate::interactive::GlobalConfig; 10 | use crate::printer::Printer; 11 | use std::str::FromStr; 12 | 13 | /// Key related commands 14 | pub fn key_command() -> App<'static, 'static> { 15 | App::new("key") 16 | .about("Some key operations, such as generating address, public key") 17 | .subcommand(SubCommand::with_name("create")) 18 | .subcommand( 19 | SubCommand::with_name("from-private").arg( 20 | Arg::with_name("private-key") 21 | .long("private-key") 22 | .takes_value(true) 23 | .required(true) 24 | .validator(|privkey| key_validator(privkey.as_ref()).map(|_| ())) 25 | .help("The private key of transaction"), 26 | ), 27 | ) 28 | .subcommand( 29 | SubCommand::with_name("pub-to-address").arg( 30 | Arg::with_name("pubkey") 31 | .long("pubkey") 32 | .takes_value(true) 33 | .required(true) 34 | .validator(|pubkey| key_validator(&pubkey).map(|_| ())) 35 | .help("Pubkey"), 36 | ), 37 | ) 38 | .subcommand( 39 | SubCommand::with_name("hash").arg( 40 | Arg::with_name("content") 41 | .long("content") 42 | .takes_value(true) 43 | .required(true) 44 | .validator(|content| is_hex(content.as_str())) 45 | .help( 46 | "Hash the content and output,\ 47 | Secp256k1 means keccak256/Ed25519 means blake2b/Sm2 means Sm3", 48 | ), 49 | ), 50 | ) 51 | .subcommand( 52 | SubCommand::with_name("verification") 53 | .arg( 54 | Arg::with_name("pubkey") 55 | .long("pubkey") 56 | .takes_value(true) 57 | .required(true) 58 | .validator(|pubkey| key_validator(&pubkey).map(|_| ())) 59 | .help("Pubkey"), 60 | ) 61 | .arg( 62 | Arg::with_name("message") 63 | .long("message") 64 | .takes_value(true) 65 | .required(true) 66 | .validator(|pubkey| h256_validator(&pubkey).map(|_| ())) 67 | .help("message"), 68 | ) 69 | .arg( 70 | Arg::with_name("signature") 71 | .long("signature") 72 | .takes_value(true) 73 | .required(true) 74 | .help("signature"), 75 | ), 76 | ) 77 | } 78 | 79 | /// Key processor 80 | pub fn key_processor( 81 | sub_matches: &ArgMatches, 82 | printer: &Printer, 83 | config: &GlobalConfig, 84 | ) -> Result<(), String> { 85 | match sub_matches.subcommand() { 86 | ("create", Some(m)) => { 87 | let encryption = encryption(m, config); 88 | let key_pair = KeyPair::new(encryption); 89 | let is_color = !sub_matches.is_present("no-color") && config.color(); 90 | printer.println(&key_pair, is_color); 91 | } 92 | ("from-private", Some(m)) => { 93 | let encryption = encryption(m, config); 94 | let private_key = m.value_of("private-key").unwrap(); 95 | let key_pair = KeyPair::from_str(remove_0x(private_key), encryption)?; 96 | let is_color = !sub_matches.is_present("no-color") && config.color(); 97 | printer.println(&key_pair, is_color); 98 | } 99 | ("pub-to-address", Some(m)) => { 100 | let encryption = encryption(m, config); 101 | let pubkey = m.value_of("pubkey").unwrap(); 102 | let address = pubkey_to_address(&PubKey::from_str(remove_0x(pubkey), encryption)?); 103 | if printer.color() { 104 | printer.println( 105 | &format!("{} 0x{:#x}", Yellow.paint("[address]:"), address), 106 | true, 107 | ); 108 | } else { 109 | printer.println(&format!("{} 0x{:#x}", "[address]:", address), false); 110 | } 111 | } 112 | ("hash", Some(m)) => { 113 | let encryption = encryption(m, config); 114 | let content = 115 | decode(remove_0x(m.value_of("content").unwrap())).map_err(|err| err.to_string())?; 116 | printer.println(&content.crypt_hash(encryption).lower_hex(), printer.color()); 117 | } 118 | ("verification", Some(m)) => { 119 | let encryption = encryption(m, config); 120 | let pubkey = PubKey::from_str(remove_0x(m.value_of("pubkey").unwrap()), encryption)?; 121 | let message = Message::from_str(remove_0x(m.value_of("message").unwrap())) 122 | .map_err(|err| err.to_string())?; 123 | let sig = Signature::from( 124 | &decode(remove_0x(m.value_of("signature").unwrap())).map_err(|e| e.to_string())?, 125 | ); 126 | println!("{}", sig.verify_public(pubkey, &message)?); 127 | } 128 | _ => { 129 | return Err(sub_matches.usage().to_owned()); 130 | } 131 | } 132 | Ok(()) 133 | } 134 | -------------------------------------------------------------------------------- /cita-cli/src/cli/store_command.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; 2 | 3 | use cita_tool::client::basic::{Client, StoreExt}; 4 | use cita_tool::remove_0x; 5 | 6 | use crate::cli::{ 7 | encryption, get_url, is_hex, key_validator, parse_address, parse_privkey, parse_u64, 8 | }; 9 | use crate::interactive::{set_output, GlobalConfig}; 10 | use crate::printer::Printer; 11 | 12 | use std::fs; 13 | use std::io::Read; 14 | 15 | /// Store data, store contract ABI subcommand 16 | pub fn store_command() -> App<'static, 'static> { 17 | let common_args = [ 18 | Arg::with_name("chain-id") 19 | .long("chain-id") 20 | .takes_value(true) 21 | .validator(|chain_id| match chain_id.parse::() { 22 | Ok(_) => Ok(()), 23 | Err(err) => Err(format!("{:?}", err)), 24 | }) 25 | .help("The chain_id of transaction"), 26 | Arg::with_name("private-key") 27 | .long("private-key") 28 | .takes_value(true) 29 | .required(true) 30 | .validator(|privkey| key_validator(privkey.as_ref()).map(|_| ())) 31 | .help("The private key of transaction"), 32 | Arg::with_name("quota") 33 | .long("quota") 34 | .takes_value(true) 35 | .validator(|quota| parse_u64(quota.as_ref()).map(|_| ())) 36 | .help("Transaction quota costs, default is 1_000_000"), 37 | ]; 38 | 39 | App::new("store") 40 | .about("Store data, store contract ABI.") 41 | .subcommand( 42 | SubCommand::with_name("data") 43 | .about("Store data to: 0xffffffffffffffffffffffffffffffffff010000") 44 | .arg( 45 | Arg::with_name("content") 46 | .long("content") 47 | .required(true) 48 | .validator(|content| is_hex(content.as_str())) 49 | .takes_value(true) 50 | .help("The content of data to store"), 51 | ) 52 | .args(&common_args), 53 | ) 54 | .subcommand( 55 | SubCommand::with_name("abi") 56 | .about("Store ABI to: 0xffffffffffffffffffffffffffffffffff010001") 57 | .arg( 58 | Arg::with_name("address") 59 | .long("address") 60 | .required(true) 61 | .validator(|address| parse_address(address.as_str())) 62 | .takes_value(true) 63 | .help("The contract address of the ABI"), 64 | ) 65 | .arg( 66 | Arg::with_name("content") 67 | .long("content") 68 | .takes_value(true) 69 | .required(true) 70 | .conflicts_with("path") 71 | .help("The content of ABI data to store (json)"), 72 | ) 73 | .arg( 74 | Arg::with_name("path") 75 | .long("path") 76 | .takes_value(true) 77 | .help("The path of ABI json file to store (.json)"), 78 | ) 79 | .group(ArgGroup::with_name("the-abi").args(&["content", "path"])) 80 | .args(&common_args), 81 | ) 82 | } 83 | 84 | /// Store data, store contract ABI processor 85 | pub fn store_processor( 86 | sub_matches: &ArgMatches, 87 | printer: &Printer, 88 | config: &mut GlobalConfig, 89 | client: Client, 90 | ) -> Result<(), String> { 91 | let debug = sub_matches.is_present("debug") || config.debug(); 92 | let mut client = client 93 | .set_debug(debug) 94 | .set_uri(get_url(sub_matches, config)); 95 | 96 | let result = match sub_matches.subcommand() { 97 | ("data", Some(m)) => { 98 | let encryption = encryption(m, config); 99 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 100 | let content = remove_0x(m.value_of("content").unwrap()); 101 | if let Some(private_key) = m.value_of("private-key") { 102 | client.set_private_key(&parse_privkey(private_key, encryption)?); 103 | } 104 | client.store_data(content, quota) 105 | } 106 | ("abi", Some(m)) => { 107 | let encryption = encryption(m, config); 108 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 109 | let content = match m.value_of("content") { 110 | Some(content) => content.to_owned(), 111 | None => { 112 | let mut abi_content = String::new(); 113 | let path = m.value_of("path").unwrap(); 114 | let mut file = fs::File::open(path).map_err(|err| format!("{}", err))?; 115 | file.read_to_string(&mut abi_content) 116 | .map_err(|err| format!("{}", err))?; 117 | abi_content 118 | } 119 | }; 120 | let address = m.value_of("address").unwrap(); 121 | if let Some(private_key) = m.value_of("private-key") { 122 | client.set_private_key(&parse_privkey(private_key, encryption)?); 123 | } 124 | client.store_abi(address, content, quota) 125 | } 126 | _ => { 127 | return Err(sub_matches.usage().to_owned()); 128 | } 129 | }; 130 | let resp = result.map_err(|err| format!("{}", err))?; 131 | let is_color = !sub_matches.is_present("no-color") && config.color(); 132 | printer.println(&resp, is_color); 133 | set_output(&resp, config); 134 | Ok(()) 135 | } 136 | -------------------------------------------------------------------------------- /cita-cli/src/cli/tx_command.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg, ArgMatches, SubCommand}; 2 | 3 | use cita_tool::client::basic::Client; 4 | use cita_tool::{encode, ProtoMessage, TransactionOptions, UnverifiedTransaction}; 5 | 6 | use crate::cli::{ 7 | encryption, get_url, is_hex, key_validator, parse_address, parse_privkey, parse_u256, 8 | parse_u32, parse_u64, 9 | }; 10 | use crate::interactive::{set_output, GlobalConfig}; 11 | use crate::printer::Printer; 12 | use std::fs::File; 13 | use std::io::Read; 14 | use std::str::FromStr; 15 | 16 | /// Transaction command 17 | pub fn tx_command() -> App<'static, 'static> { 18 | App::new("tx") 19 | .about("Construct transactions, send signed transactions etc.") 20 | .subcommand( 21 | SubCommand::with_name("make") 22 | .about("Construct transaction") 23 | .arg( 24 | Arg::with_name("code") 25 | .long("code") 26 | .default_value("0x") 27 | .takes_value(true) 28 | .validator(|code| is_hex(code.as_str())) 29 | .help("Binary content of the transaction, default is empty"), 30 | ) 31 | .arg( 32 | Arg::with_name("address") 33 | .long("address") 34 | .default_value("0x") 35 | .takes_value(true) 36 | .validator(|address| parse_address(address.as_str())) 37 | .help( 38 | "The address of the invoking contract, default is empty to \ 39 | create contract", 40 | ), 41 | ) 42 | .arg( 43 | Arg::with_name("height") 44 | .long("height") 45 | .takes_value(true) 46 | .validator(|height| parse_u64(height.as_ref()).map(|_| ())) 47 | .help("Current chain height, default query to the chain"), 48 | ) 49 | .arg( 50 | Arg::with_name("chain-id") 51 | .long("chain-id") 52 | .takes_value(true) 53 | .validator(|chain_id| parse_u256(chain_id.as_ref()).map(|_| ())) 54 | .help("The chain_id of transaction, default query to the chain"), 55 | ) 56 | .arg( 57 | Arg::with_name("quota") 58 | .long("quota") 59 | .takes_value(true) 60 | .validator(|quota| parse_u64(quota.as_ref()).map(|_| ())) 61 | .help("Transaction quota costs, default is 1_000_000"), 62 | ) 63 | .arg( 64 | Arg::with_name("value") 65 | .long("value") 66 | .takes_value(true) 67 | .validator(|value| parse_u256(value.as_ref()).map(|_| ())) 68 | .help("The value to send, default is 0"), 69 | ) 70 | .arg( 71 | Arg::with_name("version") 72 | .long("version") 73 | .takes_value(true) 74 | .validator(|version| parse_u32(version.as_str()).map(|_| ())) 75 | .help("The version of transaction, default is 0"), 76 | ), 77 | ) 78 | .subcommand( 79 | SubCommand::with_name("sendSignedTransaction") 80 | .about("Send signed transaction") 81 | .arg( 82 | Arg::with_name("byte-code") 83 | .long("byte-code") 84 | .takes_value(true) 85 | .validator(|code| is_hex(code.as_str())) 86 | .required(true) 87 | .help("Signed transaction binary data"), 88 | ), 89 | ) 90 | .subcommand( 91 | SubCommand::with_name("sendTransaction") 92 | .about("Send unsigned transaction") 93 | .arg( 94 | Arg::with_name("byte-code") 95 | .long("byte-code") 96 | .takes_value(true) 97 | .validator(|code| is_hex(code.as_str())) 98 | .required(true) 99 | .help("Unsigned transaction binary data"), 100 | ) 101 | .arg( 102 | Arg::with_name("private-key") 103 | .long("private-key") 104 | .validator(|private| key_validator(private.as_str()).map(|_| ())) 105 | .takes_value(true) 106 | .required(true) 107 | .help("Transfer Account Private Key"), 108 | ), 109 | ) 110 | .subcommand( 111 | SubCommand::with_name("decode-unverifiedTransaction") 112 | .about("Decode unverifiedTransaction") 113 | .arg( 114 | Arg::with_name("content") 115 | .long("content") 116 | .takes_value(true) 117 | .validator(|content| is_hex(content.as_str())) 118 | .conflicts_with("file") 119 | .required(true) 120 | .help("UnverifiedTransaction content"), 121 | ) 122 | .arg( 123 | Arg::with_name("file") 124 | .long("file") 125 | .takes_value(true) 126 | .help("content data file path"), 127 | ), 128 | ) 129 | } 130 | 131 | pub fn tx_processor( 132 | sub_matches: &ArgMatches, 133 | printer: &Printer, 134 | config: &mut GlobalConfig, 135 | client: Client, 136 | ) -> Result<(), String> { 137 | let debug = sub_matches.is_present("debug") || config.debug(); 138 | let is_color = !sub_matches.is_present("no-color") && config.color(); 139 | let mut client = client 140 | .set_debug(debug) 141 | .set_uri(get_url(sub_matches, config)); 142 | 143 | let result = match sub_matches.subcommand() { 144 | ("make", Some(m)) => { 145 | if let Some(chain_id) = m.value_of("chain-id").map(|s| parse_u256(s).unwrap()) { 146 | client.set_chain_id(chain_id); 147 | } 148 | let code = m.value_of("code").unwrap(); 149 | let address = m.value_of("address").unwrap(); 150 | let current_height = m.value_of("height").map(|s| parse_u64(s).unwrap()); 151 | let quota = m.value_of("quota").map(|s| parse_u64(s).unwrap()); 152 | let value = m.value_of("value").map(|value| parse_u256(value).unwrap()); 153 | let version = m 154 | .value_of("version") 155 | .map(|version| parse_u32(version).unwrap()); 156 | let tx_options = TransactionOptions::new() 157 | .set_code(code) 158 | .set_address(address) 159 | .set_current_height(current_height) 160 | .set_quota(quota) 161 | .set_value(value) 162 | .set_version(version); 163 | let tx = client 164 | .generate_transaction(tx_options) 165 | .map_err(|err| format!("{}", err))?; 166 | printer.println( 167 | &format!( 168 | "0x{}", 169 | encode(tx.write_to_bytes().map_err(|err| format!("{}", err))?) 170 | ), 171 | is_color, 172 | ); 173 | return Ok(()); 174 | } 175 | ("sendSignedTransaction", Some(m)) => { 176 | let byte_code = m.value_of("byte-code").unwrap(); 177 | client.send_signed_transaction(byte_code) 178 | } 179 | ("sendTransaction", Some(m)) => { 180 | let encryption = encryption(sub_matches, config); 181 | if let Some(private_key) = m.value_of("private-key") { 182 | client.set_private_key(&parse_privkey(private_key, encryption)?); 183 | } 184 | let byte_code = m.value_of("byte-code").unwrap(); 185 | client.send_transaction(byte_code) 186 | } 187 | ("decode-unverifiedTransaction", Some(m)) => { 188 | let encryption = encryption(sub_matches, config); 189 | let content = m.value_of("content"); 190 | let content_file = m.value_of("file"); 191 | let mut content_reader = get_content(content_file, content)?; 192 | let mut content_data = String::new(); 193 | content_reader 194 | .read_to_string(&mut content_data) 195 | .map_err(|err| format!("{}", err))?; 196 | let content_data = content_data.trim(); 197 | let tx = 198 | UnverifiedTransaction::from_str(&content_data).map_err(|err| format!("{}", err))?; 199 | printer.println(&tx.to_json(encryption)?, is_color); 200 | return Ok(()); 201 | } 202 | _ => { 203 | return Err(sub_matches.usage().to_owned()); 204 | } 205 | }; 206 | let resp = result.map_err(|err| format!("{}", err))?; 207 | printer.println(&resp, is_color); 208 | set_output(&resp, config); 209 | Ok(()) 210 | } 211 | 212 | fn get_content(path: Option<&str>, content: Option<&str>) -> Result, String> { 213 | match content { 214 | Some(data) => Ok(Box::new(::std::io::Cursor::new(data.to_owned()))), 215 | None => { 216 | let file = match path { 217 | Some(path) => File::open(path).map_err(|err| format!("{}", err))?, 218 | None => return Err("No input content".to_owned()), 219 | }; 220 | Ok(Box::new(file)) 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /cita-cli/src/cli/util.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use clap::{App, ArgMatches}; 4 | 5 | use cita_tool::{remove_0x, Address, Encryption, PrivateKey, H256, H512, U256}; 6 | 7 | use crate::interactive::GlobalConfig; 8 | 9 | /// Get url from arg match 10 | pub fn get_url<'a>(m: &'a ArgMatches, config: &'a GlobalConfig) -> &'a str { 11 | match m.value_of("url") { 12 | Some(url) => url, 13 | _ => { 14 | if m.subcommand().1.is_some() { 15 | get_url(m.subcommand().1.unwrap(), config) 16 | } else { 17 | config.get_url().as_str() 18 | } 19 | } 20 | } 21 | } 22 | 23 | /// the hexadecimal or numeric type string resolves to u64 24 | pub fn parse_u64(height: &str) -> Result { 25 | match is_hex(height) { 26 | Ok(()) => Ok(u64::from_str_radix(remove_0x(height), 16).map_err(|err| format!("{}", err))?), 27 | _ => match height.parse::() { 28 | Ok(number) => Ok(number), 29 | Err(e) => Err(format!("{:?}", e)), 30 | }, 31 | } 32 | } 33 | 34 | /// the hexadecimal or numeric type string resolves to u32 35 | pub fn parse_u32(value: &str) -> Result { 36 | match is_hex(value) { 37 | Ok(()) => Ok(u32::from_str_radix(remove_0x(value), 16).map_err(|err| format!("{}", err))?), 38 | _ => match value.parse::() { 39 | Ok(number) => Ok(number), 40 | Err(e) => Err(format!("{:?}", e)), 41 | }, 42 | } 43 | } 44 | 45 | /// Attempt to resolve the private key 46 | pub fn parse_privkey(hash: &str, encryption: Encryption) -> Result { 47 | is_hex(hash)?; 48 | Ok(PrivateKey::from_str(remove_0x(hash), encryption)?) 49 | } 50 | 51 | pub fn key_validator(hash: &str) -> Result<(), String> { 52 | is_hex(hash)?; 53 | if hash.len() > 66 { 54 | h512_validator(hash) 55 | } else { 56 | h256_validator(hash) 57 | } 58 | } 59 | 60 | pub fn is_hex(hex: &str) -> Result<(), String> { 61 | let tmp = hex.as_bytes(); 62 | if tmp.len() < 2 { 63 | Err("Must be a hexadecimal string".to_string()) 64 | } else if tmp[..2] == b"0x"[..] || tmp[..2] == b"0X"[..] { 65 | Ok(()) 66 | } else { 67 | Err("Must hex string".to_string()) 68 | } 69 | } 70 | 71 | pub fn parse_height(height: &str) -> Result<(), String> { 72 | match height { 73 | "latest" | "earliest" | "pending" => Ok(()), 74 | _ => match parse_u64(height) { 75 | Ok(_) => Ok(()), 76 | Err(e) => Err(format!("{:?}", e)), 77 | }, 78 | } 79 | } 80 | 81 | pub fn parse_u256(value: &str) -> Result { 82 | match is_hex(value) { 83 | Ok(_) => Ok(U256::from_str(remove_0x(value)) 84 | .map_err(|_| String::from("Value can't parse into u256"))?), 85 | Err(_) => { 86 | Ok(U256::from_dec_str(value) 87 | .map_err(|_| String::from("Value can't parse into u256"))?) 88 | } 89 | } 90 | } 91 | 92 | pub fn h256_validator(value: &str) -> Result<(), String> { 93 | is_hex(value)?; 94 | H256::from_str(remove_0x(value)) 95 | .map(|_| ()) 96 | .map_err(|err| format!("{}", err)) 97 | } 98 | 99 | pub fn h512_validator(value: &str) -> Result<(), String> { 100 | is_hex(value)?; 101 | H512::from_str(remove_0x(value)) 102 | .map(|_| ()) 103 | .map_err(|err| format!("{}", err)) 104 | } 105 | 106 | pub fn parse_address(value: &str) -> Result<(), String> { 107 | is_hex(value)?; 108 | if remove_0x(value).is_empty() { 109 | return Ok(()); 110 | } 111 | Address::from_str(remove_0x(value)) 112 | .map(|_| ()) 113 | .map_err(|err| err.to_string()) 114 | } 115 | 116 | pub fn encryption(m: &ArgMatches, config: &GlobalConfig) -> Encryption { 117 | match m.value_of("algorithm") { 118 | Some(v) => Encryption::from_str(v).unwrap(), 119 | None => config.encryption(), 120 | } 121 | } 122 | 123 | /// Search command tree 124 | pub fn search_app<'a, 'b>( 125 | app: &App<'a, 'b>, 126 | prefix: &Option>, 127 | commands: &mut Vec>, 128 | ) { 129 | for inner_app in &app.p.subcommands { 130 | if inner_app.p.subcommands.is_empty() { 131 | if prefix.is_some() { 132 | let mut sub_command = prefix.clone().unwrap(); 133 | sub_command.push(inner_app.p.meta.name.clone()); 134 | commands.push(sub_command); 135 | } else { 136 | commands.push(vec![inner_app.p.meta.name.clone()]); 137 | } 138 | } else { 139 | let prefix: Option> = if prefix.is_some() { 140 | prefix.clone().map(|mut x| { 141 | x.push(inner_app.p.meta.name.clone()); 142 | x 143 | }) 144 | } else { 145 | Some(vec![inner_app.p.meta.name.clone()]) 146 | }; 147 | 148 | search_app(inner_app, &prefix, commands); 149 | }; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /cita-cli/src/json_color.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | use serde::ser::Serialize; 3 | use serde_json::ser::{CharEscape, Formatter, Serializer}; 4 | use serde_json::value::Value; 5 | 6 | use std::io::{Result, Write}; 7 | use std::str; 8 | 9 | macro_rules! colorize { 10 | ($s:expr, $color:expr) => {{ 11 | let colored_string = match $color { 12 | Color::Black => $s.black(), 13 | Color::Blue => $s.blue(), 14 | Color::Cyan => $s.cyan(), 15 | Color::Green => $s.green(), 16 | Color::Magenta => $s.magenta(), 17 | Color::Purple => $s.purple(), 18 | Color::Red => $s.red(), 19 | Color::White => $s.white(), 20 | Color::Yellow => $s.yellow(), 21 | 22 | Color::Plain => $s.normal(), 23 | }; 24 | 25 | colored_string.to_string() 26 | }}; 27 | } 28 | 29 | /// The set of available colors for the various JSON components. 30 | #[derive(Clone)] 31 | pub enum Color { 32 | #[allow(dead_code)] 33 | Black, 34 | Blue, 35 | Cyan, 36 | Green, 37 | Magenta, 38 | #[allow(dead_code)] 39 | Purple, 40 | Red, 41 | #[allow(dead_code)] 42 | White, 43 | Yellow, 44 | 45 | /// Default color 46 | Plain, 47 | } 48 | 49 | impl Default for Color { 50 | fn default() -> Self { 51 | Color::Plain 52 | } 53 | } 54 | 55 | #[derive(Default)] 56 | pub struct ColorizerBuilder { 57 | null: Color, 58 | boolean: Color, 59 | number: Color, 60 | string: Color, 61 | key: Color, 62 | escape_sequence: Color, 63 | } 64 | 65 | impl ColorizerBuilder { 66 | fn new() -> Self { 67 | Default::default() 68 | } 69 | 70 | /// Sets the color of the null value. 71 | pub fn null(&mut self, color: Color) -> &mut Self { 72 | self.null = color; 73 | self 74 | } 75 | 76 | /// Sets the color of boolean values. 77 | pub fn boolean(&mut self, color: Color) -> &mut Self { 78 | self.boolean = color; 79 | self 80 | } 81 | 82 | /// Sets the color of number values. 83 | pub fn number(&mut self, color: Color) -> &mut Self { 84 | self.number = color; 85 | self 86 | } 87 | 88 | /// Sets the color of string values. 89 | pub fn string(&mut self, color: Color) -> &mut Self { 90 | self.string = color; 91 | self 92 | } 93 | 94 | /// Sets the color of JSON object keys. 95 | pub fn key(&mut self, color: Color) -> &mut Self { 96 | self.key = color; 97 | self 98 | } 99 | 100 | /// Sets the color of escape sequences within string values. 101 | pub fn escape_sequence(&mut self, color: Color) -> &mut Self { 102 | self.escape_sequence = color; 103 | self 104 | } 105 | 106 | /// Constructs a new Colorizer. 107 | pub fn build(&self) -> Colorizer { 108 | Colorizer { 109 | null: self.null.clone(), 110 | boolean: self.boolean.clone(), 111 | number: self.number.clone(), 112 | string: self.string.clone(), 113 | key: self.key.clone(), 114 | escape_sequence: self.escape_sequence.clone(), 115 | indent_level: 0, 116 | current_is_key: false, 117 | } 118 | } 119 | } 120 | 121 | /// A struct representing a specific configuration of colors for the various JSON components. 122 | #[derive(Clone, Default)] 123 | pub struct Colorizer { 124 | pub null: Color, 125 | pub boolean: Color, 126 | pub number: Color, 127 | pub string: Color, 128 | pub key: Color, 129 | escape_sequence: Color, 130 | indent_level: usize, 131 | current_is_key: bool, 132 | } 133 | 134 | impl Colorizer { 135 | /// Start builder a new Colorizer. 136 | pub fn builder() -> ColorizerBuilder { 137 | ColorizerBuilder::new() 138 | } 139 | 140 | /// Creates a new Colorizer with a predefined set of colors for the various JSON components. 141 | /// 142 | /// Use this if you want your JSON to be colored, but don't care about the specific colors. 143 | pub fn arbitrary() -> Self { 144 | Colorizer::builder() 145 | .null(Color::Cyan) 146 | .boolean(Color::Yellow) 147 | .number(Color::Magenta) 148 | .string(Color::Green) 149 | .key(Color::Blue) 150 | .escape_sequence(Color::Red) 151 | .build() 152 | } 153 | 154 | /// Colorize a JSON string. Currently, all strings will be pretty-printed (with indentation and 155 | /// spacing). 156 | /// 157 | /// # Errors 158 | /// 159 | /// An error is returned if the string is invalid JSON or an I/O error occurs. 160 | pub fn colorize_json_str(&self, s: &str) -> Result { 161 | let value: Value = ::serde_json::from_str(s)?; 162 | self.colorize_json_value(&value) 163 | } 164 | 165 | /// An error is returned if the string is invalid JSON or an I/O error occurs. 166 | pub fn colorize_json_value(&self, value: &Value) -> Result { 167 | let vec = self.to_vec(value)?; 168 | let string = unsafe { String::from_utf8_unchecked(vec) }; 169 | Ok(string) 170 | } 171 | 172 | fn to_vec(&self, value: &T) -> Result> 173 | where 174 | T: Serialize, 175 | { 176 | let mut writer = Vec::with_capacity(128); 177 | 178 | self.to_writer(&mut writer, value)?; 179 | Ok(writer) 180 | } 181 | 182 | fn to_writer(&self, writer: &mut W, value: &T) -> Result<()> 183 | where 184 | W: Write, 185 | T: Serialize, 186 | { 187 | let mut ser = Serializer::with_formatter(writer, self.clone()); 188 | value.serialize(&mut ser)?; 189 | Ok(()) 190 | } 191 | 192 | #[inline] 193 | fn get_indentation(&self) -> String { 194 | (0..self.indent_level * 2).map(|_| ' ').collect() 195 | } 196 | 197 | #[inline] 198 | fn get_string_color(&self) -> &Color { 199 | if self.current_is_key { 200 | &self.key 201 | } else { 202 | &self.string 203 | } 204 | } 205 | } 206 | 207 | impl Formatter for Colorizer { 208 | fn write_null(&mut self, writer: &mut W) -> Result<()> 209 | where 210 | W: Write, 211 | { 212 | write!(writer, "{}", colorize!("null", &self.null)) 213 | } 214 | 215 | fn write_bool(&mut self, writer: &mut W, value: bool) -> Result<()> 216 | where 217 | W: Write, 218 | { 219 | let value_as_string = format!("{}", value); 220 | write!(writer, "{}", colorize!(&value_as_string, &self.boolean)) 221 | } 222 | 223 | fn write_i8(&mut self, writer: &mut W, value: i8) -> Result<()> 224 | where 225 | W: Write, 226 | { 227 | let value_as_string = format!("{}", value); 228 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 229 | } 230 | 231 | fn write_i16(&mut self, writer: &mut W, value: i16) -> Result<()> 232 | where 233 | W: Write, 234 | { 235 | let value_as_string = format!("{}", value); 236 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 237 | } 238 | 239 | fn write_i32(&mut self, writer: &mut W, value: i32) -> Result<()> 240 | where 241 | W: Write, 242 | { 243 | let value_as_string = format!("{}", value); 244 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 245 | } 246 | 247 | fn write_i64(&mut self, writer: &mut W, value: i64) -> Result<()> 248 | where 249 | W: Write, 250 | { 251 | let value_as_string = format!("{}", value); 252 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 253 | } 254 | 255 | fn write_u8(&mut self, writer: &mut W, value: u8) -> Result<()> 256 | where 257 | W: Write, 258 | { 259 | let value_as_string = format!("{}", value); 260 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 261 | } 262 | 263 | fn write_u16(&mut self, writer: &mut W, value: u16) -> Result<()> 264 | where 265 | W: Write, 266 | { 267 | let value_as_string = format!("{}", value); 268 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 269 | } 270 | 271 | fn write_u32(&mut self, writer: &mut W, value: u32) -> Result<()> 272 | where 273 | W: Write, 274 | { 275 | let value_as_string = format!("{}", value); 276 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 277 | } 278 | 279 | fn write_u64(&mut self, writer: &mut W, value: u64) -> Result<()> 280 | where 281 | W: Write, 282 | { 283 | let value_as_string = format!("{}", value); 284 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 285 | } 286 | 287 | fn write_f32(&mut self, writer: &mut W, value: f32) -> Result<()> 288 | where 289 | W: Write, 290 | { 291 | let value_as_string = format!("{}", value); 292 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 293 | } 294 | 295 | fn write_f64(&mut self, writer: &mut W, value: f64) -> Result<()> 296 | where 297 | W: Write, 298 | { 299 | let value_as_string = format!("{}", value); 300 | write!(writer, "{}", colorize!(&value_as_string, &self.number)) 301 | } 302 | 303 | fn begin_string(&mut self, writer: &mut W) -> Result<()> 304 | where 305 | W: Write, 306 | { 307 | write!(writer, "{}", colorize!("\"", self.get_string_color())) 308 | } 309 | 310 | fn end_string(&mut self, writer: &mut W) -> Result<()> 311 | where 312 | W: Write, 313 | { 314 | write!(writer, "{}", colorize!("\"", self.get_string_color())) 315 | } 316 | 317 | fn write_string_fragment(&mut self, writer: &mut W, fragment: &str) -> Result<()> 318 | where 319 | W: Write, 320 | { 321 | write!(writer, "{}", colorize!(fragment, self.get_string_color())) 322 | } 323 | 324 | fn write_char_escape( 325 | &mut self, 326 | writer: &mut W, 327 | char_escape: CharEscape, 328 | ) -> Result<()> 329 | where 330 | W: Write, 331 | { 332 | let s = match char_escape { 333 | CharEscape::Quote => "\\\"", 334 | CharEscape::ReverseSolidus => "\\\\", 335 | CharEscape::Solidus => "\\/", 336 | CharEscape::Backspace => "\\b", 337 | CharEscape::FormFeed => "\\f", 338 | CharEscape::LineFeed => "\\n", 339 | CharEscape::CarriageReturn => "\\r", 340 | CharEscape::Tab => "\\t", 341 | CharEscape::AsciiControl(byte) => { 342 | let hex_digits = [ 343 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 344 | ]; 345 | 346 | let mut bytes = "\\u00".to_string(); 347 | bytes.push(hex_digits[(byte >> 4) as usize]); 348 | bytes.push(hex_digits[(byte & 0xF) as usize]); 349 | 350 | return write!(writer, "{}", colorize!(bytes, &self.escape_sequence)); 351 | } 352 | }; 353 | 354 | write!(writer, "{}", colorize!(s, &self.escape_sequence)) 355 | } 356 | 357 | fn begin_array(&mut self, writer: &mut W) -> Result<()> 358 | where 359 | W: Write, 360 | { 361 | self.indent_level += 1; 362 | write!(writer, "[") 363 | } 364 | 365 | fn end_array(&mut self, writer: &mut W) -> Result<()> 366 | where 367 | W: Write, 368 | { 369 | self.indent_level -= 1; 370 | write!(writer, "\n{}]", self.get_indentation()) 371 | } 372 | 373 | fn begin_array_value(&mut self, writer: &mut W, first: bool) -> Result<()> 374 | where 375 | W: Write, 376 | { 377 | if !first { 378 | write!(writer, ",")?; 379 | } 380 | 381 | write!(writer, "\n{}", self.get_indentation()) 382 | } 383 | 384 | fn begin_object_key(&mut self, writer: &mut W, first: bool) -> Result<()> 385 | where 386 | W: Write, 387 | { 388 | if !first { 389 | write!(writer, ",")?; 390 | } 391 | 392 | self.current_is_key = true; 393 | 394 | write!(writer, "\n{}", self.get_indentation()) 395 | } 396 | 397 | fn end_object_key(&mut self, _writer: &mut W) -> Result<()> 398 | where 399 | W: Write, 400 | { 401 | self.current_is_key = false; 402 | Ok(()) 403 | } 404 | 405 | fn begin_object_value(&mut self, writer: &mut W) -> Result<()> 406 | where 407 | W: Write, 408 | { 409 | write!(writer, ": ") 410 | } 411 | 412 | fn begin_object(&mut self, writer: &mut W) -> Result<()> 413 | where 414 | W: Write, 415 | { 416 | self.indent_level += 1; 417 | write!(writer, "{{") 418 | } 419 | 420 | fn end_object(&mut self, writer: &mut W) -> Result<()> 421 | where 422 | W: Write, 423 | { 424 | self.indent_level -= 1; 425 | write!(writer, "\n{}}}", self.get_indentation()) 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /cita-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | mod cli; 4 | mod interactive; 5 | mod json_color; 6 | mod printer; 7 | 8 | use std::collections::HashMap; 9 | use std::env; 10 | use std::iter::FromIterator; 11 | use std::process; 12 | use std::rc::Rc; 13 | 14 | use cita_tool::client::basic::Client; 15 | use clap::crate_version; 16 | use dotenv::dotenv; 17 | 18 | include!(concat!(env!("OUT_DIR"), "/build_info.rs")); 19 | 20 | use crate::cli::{ 21 | abi_processor, amend_processor, benchmark_processor, build_cli, completion_processor, 22 | contract_processor, key_processor, rpc_processor, search_processor, store_processor, 23 | transfer_processor, tx_processor, 24 | }; 25 | use crate::interactive::GlobalConfig; 26 | use crate::printer::Printer; 27 | 28 | const ENV_JSONRPC_URL: &str = "JSONRPC_URL"; 29 | const DEFAULT_JSONRPC_URL: &str = "http://127.0.0.1:1337"; 30 | 31 | fn main() { 32 | dotenv().ok(); 33 | let version = format!( 34 | "{}-{}, {}", 35 | crate_version!(), 36 | get_commit_id(), 37 | feature_version() 38 | ); 39 | 40 | let mut env_map: HashMap = HashMap::from_iter(env::vars()); 41 | let default_jsonrpc_url = env_map 42 | .remove(ENV_JSONRPC_URL) 43 | .unwrap_or_else(|| DEFAULT_JSONRPC_URL.to_owned()); 44 | 45 | let printer = Printer::default(); 46 | let mut config = GlobalConfig::new(default_jsonrpc_url.to_string()); 47 | let mut parser = build_cli(version.as_str()); 48 | let matches = parser.clone().get_matches(); 49 | let client = Client::new(); 50 | 51 | if let Err(err) = match matches.subcommand() { 52 | ("rpc", Some(m)) => rpc_processor(m, &printer, &mut config, client), 53 | ("ethabi", Some(m)) => abi_processor(m, &printer, &config), 54 | ("key", Some(m)) => key_processor(m, &printer, &config), 55 | ("scm", Some(m)) => contract_processor(m, &printer, &mut config, client), 56 | ("transfer", Some(m)) => transfer_processor(m, &printer, &mut config, client), 57 | ("store", Some(m)) => store_processor(m, &printer, &mut config, client), 58 | ("amend", Some(m)) => amend_processor(m, &printer, &mut config, client), 59 | ("search", Some(m)) => { 60 | search_processor(&parser, m); 61 | Ok(()) 62 | } 63 | ("tx", Some(m)) => tx_processor(m, &printer, &mut config, client), 64 | ("benchmark", Some(m)) => benchmark_processor(m, &printer, &config, client), 65 | ("completions", Some(m)) => { 66 | completion_processor(&mut parser, m); 67 | Ok(()) 68 | } 69 | _ => { 70 | if let Err(err) = interactive::start(&default_jsonrpc_url, &client) { 71 | eprintln!("Something error: kind {:?}, message {}", err.kind(), err) 72 | } 73 | Ok(()) 74 | } 75 | } { 76 | printer.eprintln(&Rc::new(err), true); 77 | process::exit(1); 78 | } 79 | } 80 | 81 | fn feature_version() -> String { 82 | if cfg!(feature = "openssl") { 83 | "use openssl".to_owned() 84 | } else { 85 | "use rustls".to_owned() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /cita-cli/src/printer.rs: -------------------------------------------------------------------------------- 1 | use std::default; 2 | use std::env; 3 | use std::io; 4 | use std::rc::Rc; 5 | 6 | use ansi_term::Colour::{Red, Yellow}; 7 | use atty; 8 | use serde_json::{self, json}; 9 | 10 | use crate::json_color::Colorizer; 11 | use cita_tool::{JsonRpcResponse, KeyPair}; 12 | 13 | pub fn is_a_tty(stderr: bool) -> bool { 14 | let stream = if stderr { 15 | atty::Stream::Stderr 16 | } else { 17 | atty::Stream::Stdout 18 | }; 19 | atty::is(stream) 20 | } 21 | 22 | pub fn is_term_dumb() -> bool { 23 | env::var("TERM").ok() == Some(String::from("dumb")) 24 | } 25 | 26 | #[derive(Copy, Clone, Debug, PartialEq)] 27 | pub enum OutputFormat { 28 | #[allow(dead_code)] 29 | Raw, 30 | Json, 31 | } 32 | 33 | #[derive(Debug, Copy, Clone, PartialEq)] 34 | pub enum ColorWhen { 35 | Auto, 36 | #[allow(dead_code)] 37 | Always, 38 | Never, 39 | } 40 | 41 | impl default::Default for ColorWhen { 42 | fn default() -> Self { 43 | let is_a_tty = is_a_tty(false); 44 | let is_term_dumb = is_term_dumb(); 45 | if is_a_tty && !is_term_dumb { 46 | ColorWhen::Auto 47 | } else { 48 | ColorWhen::Never 49 | } 50 | } 51 | } 52 | 53 | pub struct Printer { 54 | format: OutputFormat, 55 | color: ColorWhen, 56 | } 57 | 58 | impl default::Default for Printer { 59 | fn default() -> Self { 60 | Printer { 61 | format: OutputFormat::Json, 62 | color: ColorWhen::default(), 63 | } 64 | } 65 | } 66 | 67 | impl Printer { 68 | pub fn color(&self) -> bool { 69 | self.color != ColorWhen::Never 70 | } 71 | 72 | pub fn switch_format(&mut self) { 73 | match self.format { 74 | OutputFormat::Raw => { 75 | self.format = OutputFormat::Json; 76 | } 77 | OutputFormat::Json => { 78 | self.format = OutputFormat::Raw; 79 | } 80 | } 81 | } 82 | 83 | #[allow(dead_code)] 84 | pub fn set_color(&mut self, color: ColorWhen) -> &mut Self { 85 | self.color = color; 86 | self 87 | } 88 | 89 | pub fn print( 90 | &self, 91 | target: &mut W, 92 | content: &P, 93 | newline: bool, 94 | color: Option, 95 | ) -> io::Result<()> { 96 | let color = match color.unwrap_or(self.color) { 97 | ColorWhen::Always | ColorWhen::Auto => true, 98 | ColorWhen::Never => false, 99 | }; 100 | target.write_all(content.rc_string(self.format, color).as_bytes())?; 101 | if newline { 102 | target.write_all(&[b'\n'])?; 103 | } 104 | Ok(()) 105 | } 106 | 107 | pub fn println(&self, content: &P, color: bool) { 108 | let stdout = io::stdout(); 109 | let color = if color { None } else { Some(ColorWhen::Never) }; 110 | self.print(&mut stdout.lock(), content, true, color) 111 | .unwrap(); 112 | } 113 | 114 | pub fn eprintln(&self, content: &P, color: bool) { 115 | let stderr = io::stderr(); 116 | if color { 117 | let prefix = Rc::new(format!("{} ", Red.paint(">>"))); 118 | self.print(&mut stderr.lock(), &prefix, false, None) 119 | .unwrap(); 120 | }; 121 | let color = if color { None } else { Some(ColorWhen::Never) }; 122 | self.print(&mut stderr.lock(), content, true, color) 123 | .unwrap(); 124 | } 125 | } 126 | 127 | pub trait Printable { 128 | fn rc_string(&self, format: OutputFormat, color: bool) -> Rc; 129 | } 130 | 131 | impl Printable for String { 132 | fn rc_string(&self, _format: OutputFormat, _color: bool) -> Rc { 133 | Rc::new(self.clone()) 134 | } 135 | } 136 | 137 | impl Printable for Rc { 138 | fn rc_string(&self, _format: OutputFormat, _color: bool) -> Rc { 139 | self.clone() 140 | } 141 | } 142 | 143 | impl Printable for JsonRpcResponse { 144 | fn rc_string(&self, _format: OutputFormat, color: bool) -> Rc { 145 | let content = format!("{:?}", self); 146 | let content = if color { 147 | Colorizer::arbitrary() 148 | .colorize_json_str(content.as_str()) 149 | .unwrap() 150 | } else { 151 | content 152 | }; 153 | Rc::new(content) 154 | } 155 | } 156 | 157 | impl Printable for serde_json::Value { 158 | fn rc_string(&self, format: OutputFormat, color: bool) -> Rc { 159 | if let (OutputFormat::Raw, serde_json::Value::String(content)) = (format, self) { 160 | return Rc::new(content.clone()); 161 | } 162 | let content = if color { 163 | Colorizer::arbitrary().colorize_json_value(self).unwrap() 164 | } else { 165 | serde_json::to_string_pretty(self).unwrap() 166 | }; 167 | Rc::new(content) 168 | } 169 | } 170 | 171 | impl Printable for KeyPair { 172 | fn rc_string(&self, format: OutputFormat, color: bool) -> Rc { 173 | match format { 174 | OutputFormat::Json => json!({ 175 | "private": format!("0x{}", self.privkey()), 176 | "public": format!("0x{}", self.pubkey()), 177 | "address": format!("0x{:x}", self.address()) 178 | }) 179 | .rc_string(format, color), 180 | OutputFormat::Raw => { 181 | let content = if color { 182 | format!( 183 | concat!("{} 0x{}\n", "{} 0x{}\n", "{} 0x{:x}"), 184 | Yellow.paint("[ private ]:"), 185 | self.privkey(), 186 | Yellow.paint("[ public ]:"), 187 | self.pubkey(), 188 | Yellow.paint("[ address ]:"), 189 | self.address() 190 | ) 191 | } else { 192 | format!( 193 | concat!("{} 0x{}\n", "{} 0x{}\n", "{} 0x{:x}"), 194 | "[ private ]:", 195 | self.privkey(), 196 | "[ public ]:", 197 | self.pubkey(), 198 | "[ address ]:", 199 | self.address() 200 | ) 201 | }; 202 | Rc::new(content) 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /cita-tool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cita-tool" 3 | version = "0.19.0" 4 | authors = ["piaoliu <441594700@qq.com>", "Qian Linfeng "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | hyper = "^0.12.1" 9 | tokio = "^0.1.7" 10 | futures = "^0.1" 11 | serde_json = "^1.0.17" 12 | serde = "^1.0.53" 13 | serde_derive = "^1.0.53" 14 | protobuf = "=2.8.1" 15 | hex = "^0.3.2" 16 | tiny-keccak = "^1.4.2" 17 | secp256k1 = { version = "0.15.0", features = ["recovery"]} 18 | blake2b_simd = "0.5.0" 19 | ed25519-dalek = "0.9.1" 20 | sha2 = "0.8.0" 21 | libsm = { version = "0.3.0", package = "cryptape-sm" } 22 | # rename to types 23 | types = { version = "^0.4.0", package = "ethereum-types"} 24 | lazy_static = "^1.0" 25 | rand = "^0.6.0" 26 | uuid = { version = "0.7", features = ["serde", "v4"] } 27 | failure = "^0.1.1" 28 | ethabi = "^8.0" 29 | tool-derive = { path = "../tool-derive" } 30 | hyper-rustls = { version = "0.16.1", optional = true } 31 | hyper-tls = { version = "^0.3", optional = true } 32 | 33 | [features] 34 | default = ["rustls"] 35 | openssl = ["hyper-tls"] 36 | rustls = ["hyper-rustls"] 37 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/Admin.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"update","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"isAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_account","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":true,"name":"_old","type":"address"},{"indexed":true,"name":"_sender","type":"address"}],"name":"AdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"errorType","type":"uint8"},{"indexed":false,"name":"msg","type":"string"}],"name":"ErrorLog","type":"event"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/Authorization.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permission","type":"address"}],"name":"cancelAuth","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"},{"name":"_permission","type":"address"}],"name":"checkPermission","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_permission","type":"address"}],"name":"queryAccounts","outputs":[{"name":"_accounts","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_permission","type":"address"}],"name":"clearAuthOfPermission","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"queryPermissions","outputs":[{"name":"_permissions","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"clearAuth","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryAllAccounts","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"},{"name":"_cont","type":"address"},{"name":"_func","type":"bytes4"}],"name":"checkResource","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permission","type":"address"}],"name":"setAuth","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_superAdmin","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":true,"name":"_permission","type":"address"}],"name":"AuthSetted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":true,"name":"_permission","type":"address"}],"name":"AuthCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"}],"name":"AuthCleared","type":"event"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/BatchTx.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"","type":"bytes"}],"name":"multiTxs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/ChainManager.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"getChainId","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint32"}],"name":"getAuthorities","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"getParentChainId","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint32"}],"name":"enableSideChain","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sideChainId","type":"uint32"},{"name":"addrs","type":"address[]"}],"name":"newSideChain","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint32"}],"name":"disableSideChain","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint32"}],"name":"sideChains","outputs":[{"name":"status","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_pid","type":"uint32"},{"name":"_addrs","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"errorType","type":"uint8"},{"indexed":false,"name":"msg","type":"string"}],"name":"ErrorLog","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/EmergencyBrake.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, {"constant":false,"inputs":[{"name":"_state","type":"bool"}],"name":"setState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/Group.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_accounts","type":"address[]"}],"name":"deleteAccounts","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"updateName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_child","type":"address"}],"name":"addChild","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"inGroup","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryInfo","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryName","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"close","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryParent","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_child","type":"address"}],"name":"deleteChild","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_accounts","type":"address[]"}],"name":"addAccounts","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryChildLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryAccounts","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryChild","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_parent","type":"address"},{"name":"_name","type":"bytes32"},{"name":"_accounts","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent","type":"address"},{"indexed":true,"name":"_name","type":"bytes32"},{"indexed":false,"name":"_accounts","type":"address[]"}],"name":"GroupNewed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_accounts","type":"address[]"}],"name":"AccountsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_accounts","type":"address[]"}],"name":"AccountsDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_oldName","type":"bytes32"},{"indexed":true,"name":"_newName","type":"bytes32"}],"name":"NameUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_child","type":"address"}],"name":"ChildDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_child","type":"address"}],"name":"ChildAdded","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/GroupManagement.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_origin","type":"address"},{"name":"_target","type":"address"},{"name":"_accounts","type":"address[]"}],"name":"addAccounts","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryGroups","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_origin","type":"address"},{"name":"_target","type":"address"},{"name":"_name","type":"bytes32"}],"name":"updateGroupName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_origin","type":"address"},{"name":"_target","type":"address"}],"name":"deleteGroup","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_origin","type":"address"},{"name":"_name","type":"bytes32"},{"name":"_accounts","type":"address[]"}],"name":"newGroup","outputs":[{"name":"new_group","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_origin","type":"address"},{"name":"_target","type":"address"},{"name":"_accounts","type":"address[]"}],"name":"deleteAccounts","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_origin","type":"address"},{"name":"_target","type":"address"}],"name":"checkScope","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_group","type":"address"}],"name":"GroupDeleted","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/NodeManager.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"deleteRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_node","type":"address"}],"name":"stakePermillage","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"createContractAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_node","type":"address"}],"name":"deleteNode","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rootGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_node","type":"address"}],"name":"getStatus","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"groupCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newPermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"permissionCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deleteGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_node","type":"address"},{"name":"stake","type":"uint64"}],"name":"setStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cancelAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listNode","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cancelRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"status","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStake","outputs":[{"name":"_stakes","type":"uint64[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deletePermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"setAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"adminAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updateRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"userManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updateGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"permissionManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authorizationAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"setRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"sendTxAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_node","type":"address"}],"name":"approveNode","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"roleAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updatePermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_nodes","type":"address[]"},{"name":"_stakes","type":"uint64[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"errorType","type":"uint8"},{"indexed":false,"name":"msg","type":"string"}],"name":"ErrorLog","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_node","type":"address"}],"name":"ApproveNode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_node","type":"address"}],"name":"DeleteNode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_node","type":"address"},{"indexed":false,"name":"_stake","type":"uint256"}],"name":"SetStake","type":"event"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/Permission.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"cont","type":"address"},{"name":"func","type":"bytes4"}],"name":"inPermission","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"updateName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryInfo","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address[]"},{"name":"","type":"bytes4[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"name":"deleteResources","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryName","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"close","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryResource","outputs":[{"name":"","type":"address[]"},{"name":"","type":"bytes4[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"name":"addResources","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_name","type":"bytes32"},{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_conts","type":"address[]"},{"indexed":false,"name":"_funcs","type":"bytes4[]"}],"name":"ResourcesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_conts","type":"address[]"},{"indexed":false,"name":"_funcs","type":"bytes4[]"}],"name":"ResourcesDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_oldName","type":"bytes32"},{"indexed":true,"name":"_name","type":"bytes32"}],"name":"NameUpdated","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/PermissionManagement.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permission","type":"address"}],"name":"setAuthorization","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permission","type":"address"}],"name":"cancelAuthorization","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permissions","type":"address[]"}],"name":"setAuthorizations","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_permission","type":"address"},{"name":"_name","type":"bytes32"}],"name":"updatePermissionName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_permission","type":"address"},{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"name":"deleteResources","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_permission","type":"address"}],"name":"deletePermission","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"clearAuthorization","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_permissions","type":"address[]"}],"name":"cancelAuthorizations","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_permission","type":"address"},{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"name":"addResources","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_conts","type":"address[]"},{"name":"_funcs","type":"bytes4[]"}],"name":"newPermission","outputs":[{"name":"id","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_permission","type":"address"}],"name":"PermissionDeleted","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/PriceManager.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"getQuotaPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_quotaPrice","type":"uint256"}],"name":"setQuotaPrice","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/QuotaManager.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"deleteRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBQL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"createContractAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rootGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"groupCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newPermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"permissionCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deleteGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_value","type":"uint256"}],"name":"setAQL","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cancelAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cancelRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deletePermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"setAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"adminAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updateRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAccounts","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"setBQL","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"getAQL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"userManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updateGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"setDefaultAQL","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"permissionManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDefaultAQL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authorizationAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"setRoleAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"sendTxAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuotas","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"updatePermissionAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_admin","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"errorType","type":"uint8"},{"indexed":false,"name":"msg","type":"string"}],"name":"ErrorLog","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_value","type":"uint256"},{"indexed":true,"name":"_sender","type":"address"}],"name":"DefaultAqlSetted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_value","type":"uint256"},{"indexed":true,"name":"_sender","type":"address"}],"name":"BqlSetted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":true,"name":"_sender","type":"address"}],"name":"AqlSetted","type":"event"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/Role.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_permissions","type":"address[]"}],"name":"addPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deleteRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_permission","type":"address"}],"name":"inPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"updateName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"queryName","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryPermissions","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"queryRole","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lengthOfPermissions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_permissions","type":"address[]"}],"name":"deletePermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_name","type":"bytes32"},{"name":"_permissions","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_oldName","type":"bytes32"},{"indexed":true,"name":"_newName","type":"bytes32"}],"name":"NameUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_permissions","type":"address[]"}],"name":"PermissionsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_permissions","type":"address[]"}],"name":"PermissionsDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_name","type":"bytes32"},{"indexed":false,"name":"_permissions","type":"address[]"}],"name":"RoleCreated","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/RoleManagement.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_roleid","type":"address"},{"name":"_permissions","type":"address[]"}],"name":"addPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_roleid","type":"address"},{"name":"_permissions","type":"address[]"}],"name":"deletePermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_roleId","type":"address"}],"name":"queryAccounts","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_roleid","type":"address"}],"name":"deleteRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_permissions","type":"address[]"}],"name":"newRole","outputs":[{"name":"roleid","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_role","type":"address"}],"name":"queryPermissions","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_role","type":"address"}],"name":"setRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_role","type":"address"}],"name":"cancelRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"clearRole","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_roleid","type":"address"},{"name":"_name","type":"bytes32"}],"name":"updateRoleName","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_account","type":"address"}],"name":"queryRoles","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":true,"name":"_role","type":"address"}],"name":"RoleSetted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"},{"indexed":true,"name":"_role","type":"address"}],"name":"RoleCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_account","type":"address"}],"name":"RoleCleared","type":"event"}] -------------------------------------------------------------------------------- /cita-tool/contract_abi/SysConfig.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"createContractAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"updateToChainIdV1","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rootGroupAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"groupCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getChainId","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"permissionCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getSendTxPermissionCheck","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getChainIdV1","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEconomicalModel","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCreateContractPermissionCheck","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_operator","type":"string"}],"name":"setOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"roleManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"sysConfigAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"adminAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDelayBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getFeeBackPlatformCheck","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"userManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"builtInPermissions","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTokenInfo","outputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"avatar","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuotaCheck","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"permissionManagementAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"authorizationAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_chainName","type":"string"}],"name":"setChainName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"sendTxAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBlockInterval","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getChainName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPermissionCheck","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_blockInterval","type":"uint64"}],"name":"setBlockInterval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getWebsite","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleAuthAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getChainOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOperator","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"roleCreatorAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAutoExec","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_website","type":"string"}],"name":"setWebsite","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_delayBlockNumber","type":"uint256"},{"name":"_chainOwner","type":"address"},{"name":"_chainName","type":"string"},{"name":"_chainId","type":"uint256"},{"name":"_operator","type":"string"},{"name":"_website","type":"string"},{"name":"_blockInterval","type":"uint64"},{"name":"_economicalModel","type":"uint8"},{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_avatar","type":"string"},{"name":"flags","type":"bool[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] 2 | -------------------------------------------------------------------------------- /cita-tool/contract_abi/VersionManager.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"getVersion","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"}, {"constant":false,"inputs":[{"name":"_version","type":"uint32"}],"name":"setVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /cita-tool/create_protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm ./src/protos/blockchain.rs 3 | protoc --rust_out ./src/protos/ ./proto/blockchain.proto 4 | -------------------------------------------------------------------------------- /cita-tool/src/abi.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Read; 3 | 4 | use crate::LowerHex; 5 | use ethabi::param_type::{ParamType, Reader}; 6 | use ethabi::token::{LenientTokenizer, StrictTokenizer, Token, Tokenizer}; 7 | use ethabi::{decode, encode, Contract, Hash}; 8 | use hex::{decode as hex_decode, encode as hex_encode}; 9 | use types::U256; 10 | 11 | use crate::error::ToolError; 12 | 13 | pub fn parse_tokens(params: &[(ParamType, &str)], lenient: bool) -> Result, ToolError> { 14 | params 15 | .iter() 16 | .map(|&(ref param, value)| { 17 | if lenient { 18 | let type_name = format!("{}", param); 19 | if type_name.starts_with("uint") && type_name.find(']').is_none() { 20 | let y = U256::from_dec_str(value) 21 | .map_err(|_| "Can't parse into u256")? 22 | .completed_lower_hex(); 23 | StrictTokenizer::tokenize(param, &y) 24 | } else if type_name.starts_with("int") && type_name.find(']').is_none() { 25 | let x = if value.starts_with('-') { 26 | let x = (!U256::from_dec_str(&value[1..]) 27 | .map_err(|_| "Can't parse into u256")? 28 | + U256::from(1)) 29 | .lower_hex(); 30 | format!("{:f>64}", x) 31 | } else { 32 | U256::from_dec_str(value) 33 | .map_err(|_| "Can't parse into u256")? 34 | .completed_lower_hex() 35 | }; 36 | StrictTokenizer::tokenize(param, &x) 37 | } else { 38 | LenientTokenizer::tokenize(param, value) 39 | } 40 | } else { 41 | StrictTokenizer::tokenize(param, value) 42 | } 43 | }) 44 | .collect::>() 45 | .map_err(|e| ToolError::Abi(e.to_string())) 46 | } 47 | 48 | /// According to the contract, encode the function and parameter values 49 | pub fn contract_encode_input( 50 | contract: &Contract, 51 | function: &str, 52 | values: &[String], 53 | lenient: bool, 54 | ) -> Result { 55 | let function = contract 56 | .function(function) 57 | .map_err(|e| ToolError::Abi(e.to_string()))? 58 | .clone(); 59 | let params: Vec<_> = function 60 | .inputs 61 | .iter() 62 | .map(|param| param.kind.clone()) 63 | .zip(values.iter().map(|v| v as &str)) 64 | .collect(); 65 | 66 | let tokens = parse_tokens(¶ms, lenient)?; 67 | let result = function 68 | .encode_input(&tokens) 69 | .map_err(|e| ToolError::Abi(e.to_string()))?; 70 | 71 | Ok(hex_encode(result)) 72 | } 73 | 74 | /// According to the contract, encode the constructor and parameter values 75 | pub fn constructor_encode_input( 76 | contract: &Contract, 77 | code: &str, 78 | values: &[String], 79 | lenient: bool, 80 | ) -> Result { 81 | match contract.constructor { 82 | Some(ref constructor) => { 83 | let params: Vec<_> = constructor 84 | .inputs 85 | .iter() 86 | .map(|param| param.kind.clone()) 87 | .zip(values.iter().map(|v| v as &str)) 88 | .collect(); 89 | let tokens = parse_tokens(¶ms, lenient)?; 90 | Ok(format!( 91 | "{}{}", 92 | code, 93 | hex_encode( 94 | constructor 95 | .encode_input(Vec::new(), &tokens) 96 | .map_err(|e| ToolError::Abi(e.to_string()))?, 97 | ) 98 | )) 99 | } 100 | None => Err(ToolError::Abi("No constructor on abi".to_string())), 101 | } 102 | } 103 | 104 | /// According to the given abi file, encode the function and parameter values 105 | pub fn encode_input( 106 | path: Option<&str>, 107 | abi: Option<&str>, 108 | function: &str, 109 | values: &[String], 110 | lenient: bool, 111 | constructor: bool, 112 | ) -> Result { 113 | let contract = 114 | Contract::load(get_abi(path, abi)?).map_err(|e| ToolError::Abi(format!("{}", e)))?; 115 | if constructor { 116 | constructor_encode_input(&contract, function, values, lenient) 117 | } else { 118 | contract_encode_input(&contract, function, values, lenient) 119 | } 120 | } 121 | 122 | /// According to type, encode the value of the parameter 123 | pub fn encode_params( 124 | types: &[String], 125 | values: &[String], 126 | lenient: bool, 127 | ) -> Result { 128 | assert_eq!(types.len(), values.len()); 129 | 130 | let types: Vec = types 131 | .iter() 132 | .map(|s| Reader::read(s)) 133 | .collect::>() 134 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 135 | 136 | let params: Vec<_> = types 137 | .into_iter() 138 | .zip(values.iter().map(|v| v as &str)) 139 | .collect(); 140 | 141 | let tokens = parse_tokens(¶ms, lenient)?; 142 | let result = encode(&tokens); 143 | 144 | Ok(hex_encode(result)) 145 | } 146 | 147 | /// According to type, decode the data 148 | pub fn decode_params(types: &[String], data: &str) -> Result, ToolError> { 149 | let types: Vec = types 150 | .iter() 151 | .map(|s| Reader::read(s)) 152 | .collect::>() 153 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 154 | 155 | let data = hex_decode(data).map_err(ToolError::Decode)?; 156 | 157 | let tokens = decode(&types, &data).map_err(|e| ToolError::Abi(format!("{}", e)))?; 158 | 159 | assert_eq!(types.len(), tokens.len()); 160 | 161 | let result = types 162 | .iter() 163 | .zip(tokens.iter()) 164 | .map(|(ty, to)| { 165 | if to.type_check(&ParamType::Bool) || format!("{}", ty) == "bool[]" { 166 | format!("{{\"{}\": {}}}", ty, to) 167 | } else { 168 | let to_str = format!("{}", to); 169 | let escaped_str = to_str.escape_default(); 170 | format!("{{\"{}\": \"{}\"}}", ty, escaped_str) 171 | } 172 | }) 173 | .collect::>(); 174 | 175 | Ok(result) 176 | } 177 | 178 | /// According to the given abi file, decode the data 179 | pub fn decode_input( 180 | path: Option<&str>, 181 | abi: Option<&str>, 182 | function: &str, 183 | data: &str, 184 | ) -> Result, ToolError> { 185 | let contract = 186 | Contract::load(get_abi(path, abi)?).map_err(|e| ToolError::Abi(format!("{}", e)))?; 187 | let function = contract 188 | .function(function) 189 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 190 | let tokens = function 191 | .decode_output(data.as_bytes()) 192 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 193 | let types = function.outputs.iter().map(|ref param| ¶m.kind); 194 | 195 | assert_eq!(types.len(), tokens.len()); 196 | 197 | let result = types 198 | .zip(tokens.iter()) 199 | .map(|(ty, to)| { 200 | if to.type_check(&ParamType::Bool) || format!("{}", ty) == "bool[]" { 201 | format!("{{\"{}\": {}}}", ty, to) 202 | } else { 203 | format!("{{\"{}\": \"{}\"}}", ty, to) 204 | } 205 | }) 206 | .collect::>(); 207 | 208 | Ok(result) 209 | } 210 | 211 | /// According to the given abi file, decode the topic 212 | pub fn decode_logs( 213 | path: Option<&str>, 214 | abi: Option<&str>, 215 | event: &str, 216 | topics: &[String], 217 | data: &str, 218 | ) -> Result, ToolError> { 219 | let contract = 220 | Contract::load(get_abi(path, abi)?).map_err(|e| ToolError::Abi(format!("{}", e)))?; 221 | let event = contract 222 | .event(event) 223 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 224 | 225 | let topics: Vec = topics 226 | .iter() 227 | .map(|t| t.parse()) 228 | .collect::>() 229 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 230 | let data = hex_decode(data).map_err(ToolError::Decode)?; 231 | let decoded = event 232 | .parse_log((topics, data).into()) 233 | .map_err(|e| ToolError::Abi(format!("{}", e)))?; 234 | 235 | let result = decoded 236 | .params 237 | .into_iter() 238 | .map(|log_param| format!("{{\"{}\": \"{}\"}}", log_param.name, log_param.value)) 239 | .collect::>(); 240 | 241 | Ok(result) 242 | } 243 | 244 | fn get_abi(path: Option<&str>, abi: Option<&str>) -> Result, ToolError> { 245 | match abi { 246 | Some(code) => Ok(Box::new(::std::io::Cursor::new(code.to_owned()))), 247 | None => { 248 | let file = match path { 249 | Some(path) => File::open(path).map_err(|e| ToolError::Abi(format!("{}", e)))?, 250 | None => return Err(ToolError::Abi("No input abi".to_string())), 251 | }; 252 | Ok(Box::new(file)) 253 | } 254 | } 255 | } 256 | 257 | #[cfg(test)] 258 | mod test { 259 | use super::{decode_params, encode_params}; 260 | 261 | #[test] 262 | fn test_encode() { 263 | let a = encode_params(&["int".to_string()], &["-100".to_string()], true).unwrap(); 264 | assert_eq!( 265 | a, 266 | "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c".to_string() 267 | ); 268 | 269 | let b = encode_params( 270 | &["int".to_string()], 271 | &["-99999999999999999999999999999999999999999999999999999999999999999999".to_string()], 272 | true, 273 | ) 274 | .unwrap(); 275 | assert_eq!( 276 | b, 277 | "fffffffc4a717738acec1362cd61555e7046d08adea4e8f00000000000000001".to_string() 278 | ); 279 | 280 | let c = encode_params(&["uint".to_string()], &["100".to_string()], true).unwrap(); 281 | assert_eq!( 282 | c, 283 | "0000000000000000000000000000000000000000000000000000000000000064".to_string() 284 | ); 285 | 286 | let d = encode_params( 287 | &["uint".to_string()], 288 | &["99999999999999999999999999999999999999999999999999999999999999999999".to_string()], 289 | true, 290 | ) 291 | .unwrap(); 292 | assert_eq!( 293 | d, 294 | "00000003b58e88c75313ec9d329eaaa18fb92f75215b170fffffffffffffffff".to_string() 295 | ); 296 | 297 | let e = encode_params(&["string".to_string()], &["\"".to_string()], true).unwrap(); 298 | assert_eq!( 299 | e, 300 | "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000012200000000000000000000000000000000000000000000000000000000000000".to_string() 301 | ); 302 | let f = decode_params(&["string".to_string()], &e).unwrap(); 303 | assert_eq!(f, ["{\"string\": \"\\\"\"}".to_string()]); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /cita-tool/src/client.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(feature = "rustls", feature = "openssl"))] 2 | compile_error!("features `rustls` and `openssl` are mutually exclusive"); 3 | 4 | /// Basic client api, for Low-level interface 5 | pub mod basic; 6 | /// System contract client api, call system contract more easy 7 | pub mod system_contract; 8 | 9 | mod transaction_option; 10 | 11 | pub use self::transaction_option::TransactionOptions; 12 | 13 | use hyper::Uri; 14 | use std::str; 15 | 16 | /// Remove hexadecimal prefix "0x" or "0X". 17 | /// Example: 18 | /// ```rust 19 | /// extern crate cita_tool; 20 | /// 21 | /// use cita_tool::remove_0x; 22 | /// 23 | /// let a = "0x0b"; 24 | /// let b = remove_0x(a); 25 | /// let c = "0X0b"; 26 | /// let d = remove_0x(c); 27 | /// assert_eq!("0b", b); 28 | /// assert_eq!("0b", d); 29 | /// println!("a = {}, b = {}, c = {}, d= {}", a, b, c, d); 30 | /// ``` 31 | #[inline] 32 | pub fn remove_0x(hex: &str) -> &str { 33 | if hex.len() >= 2 { 34 | let tmp = hex.as_bytes(); 35 | if tmp[..2] == b"0x"[..] || tmp[..2] == b"0X"[..] { 36 | return str::from_utf8(&tmp[2..]).unwrap(); 37 | } 38 | } 39 | hex 40 | } 41 | 42 | /// Verify the validity of the url address 43 | #[inline] 44 | pub fn parse_url(url: &str) -> Result { 45 | url.parse().map_err(|_| "Invalid address".to_string()) 46 | } 47 | -------------------------------------------------------------------------------- /cita-tool/src/client/transaction_option.rs: -------------------------------------------------------------------------------- 1 | use types::U256; 2 | 3 | /// Transaction parameter option 4 | #[derive(Clone, Copy, Debug)] 5 | pub struct TransactionOptions<'a> { 6 | code: &'a str, 7 | address: &'a str, 8 | current_height: Option, 9 | quota: Option, 10 | value: Option, 11 | version: Option, 12 | } 13 | 14 | impl<'a> TransactionOptions<'a> { 15 | /// Default option 16 | pub fn new() -> Self { 17 | TransactionOptions { 18 | code: "0x", 19 | address: "0x", 20 | current_height: None, 21 | quota: None, 22 | value: None, 23 | version: None, 24 | } 25 | } 26 | 27 | /// Set code. Transaction content, default is "0x" 28 | pub fn set_code(mut self, code: &'a str) -> Self { 29 | self.code = code; 30 | self 31 | } 32 | 33 | /// Get code 34 | pub fn code(&self) -> &str { 35 | self.code 36 | } 37 | 38 | /// Set address. Destination address (account or contract address), 39 | /// default is "0x", which creates the contract 40 | pub fn set_address(mut self, address: &'a str) -> Self { 41 | self.address = address; 42 | self 43 | } 44 | 45 | /// Get address 46 | pub fn address(&self) -> &str { 47 | self.address 48 | } 49 | 50 | /// Set current height. Used to set until_block. 51 | /// Set the current chain height, the default is None, 52 | /// automatically query before the transaction to get the current chain height 53 | pub fn set_current_height(mut self, height: Option) -> Self { 54 | self.current_height = height; 55 | self 56 | } 57 | 58 | /// Get current height 59 | pub fn current_height(&self) -> Option { 60 | self.current_height 61 | } 62 | 63 | /// Set quota. Transaction consumption quota limit 64 | pub fn set_quota(mut self, quota: Option) -> Self { 65 | self.quota = quota; 66 | self 67 | } 68 | 69 | /// Get quota 70 | pub fn quota(&self) -> Option { 71 | self.quota 72 | } 73 | 74 | /// Set value. Transaction transfer amount 75 | pub fn set_value(mut self, value: Option) -> Self { 76 | self.value = value; 77 | self 78 | } 79 | 80 | /// Get value 81 | pub fn value(&self) -> Option { 82 | self.value 83 | } 84 | 85 | /// Set version. 86 | pub fn set_version(mut self, version: Option) -> Self { 87 | self.version = version; 88 | self 89 | } 90 | 91 | /// Get version 92 | pub fn version(&self) -> Option { 93 | self.version 94 | } 95 | 96 | /// Restore initialization status 97 | pub fn clear(&mut self) { 98 | self.value = None; 99 | self.quota = None; 100 | self.current_height = None; 101 | self.address = "0x"; 102 | self.code = "0x"; 103 | self.version = None 104 | } 105 | } 106 | 107 | impl Default for TransactionOptions<'static> { 108 | fn default() -> Self { 109 | TransactionOptions::new() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /cita-tool/src/crypto.rs: -------------------------------------------------------------------------------- 1 | mod cita_ed25519; 2 | mod cita_secp256k1; 3 | mod cita_sm2; 4 | mod crypto_trait; 5 | 6 | use hex::encode; 7 | use std::fmt; 8 | use std::str::FromStr; 9 | 10 | pub use self::cita_ed25519::{ed25519_sign, Ed25519KeyPair, Ed25519Signature}; 11 | pub use self::cita_secp256k1::{secp256k1_sign, Secp256k1KeyPair, Secp256k1Signature}; 12 | pub use self::cita_sm2::{sm2_sign, Sm2KeyPair, Sm2Signature}; 13 | pub use self::crypto_trait::{CreateKey, Error, Hashable}; 14 | use crate::LowerHex; 15 | use types::{Address, H256, H512}; 16 | 17 | /// Secp256k1 Private key 18 | pub type Secp256k1PrivKey = H256; 19 | /// Secp256k1 Public key 20 | pub type Secp256k1PubKey = H512; 21 | /// Sign Message 22 | pub type Message = H256; 23 | /// Sm2 Private key 24 | pub type Sm2Privkey = H256; 25 | /// Sm2 Public key 26 | pub type Sm2Pubkey = H512; 27 | 28 | /// Ed25519 Private key 29 | pub type Ed25519PrivKey = H512; 30 | /// Ed25519 Public key 31 | pub type Ed25519PubKey = H256; 32 | 33 | /// Generate Address from public key 34 | pub fn pubkey_to_address(pubkey: &PubKey) -> Address { 35 | match pubkey { 36 | PubKey::Secp256k1(pubkey) => Address::from(pubkey.crypt_hash(Encryption::Secp256k1)), 37 | PubKey::Ed25519(pubkey) => Address::from(pubkey.crypt_hash(Encryption::Ed25519)), 38 | PubKey::Sm2(pubkey) => Address::from(pubkey.crypt_hash(Encryption::Sm2)), 39 | PubKey::Null => Address::default(), 40 | } 41 | } 42 | 43 | /// Sign data 44 | pub fn sign(privkey: &PrivateKey, message: &Message) -> Signature { 45 | match privkey { 46 | PrivateKey::Secp256k1(pk) => Signature::Secp256k1(secp256k1_sign(pk, message).unwrap()), 47 | PrivateKey::Ed25519(pk) => Signature::Ed25519(ed25519_sign(pk, message).unwrap()), 48 | PrivateKey::Sm2(pk) => Signature::Sm2(sm2_sign(pk, message).unwrap()), 49 | PrivateKey::Null => Signature::Null, 50 | } 51 | } 52 | 53 | /// Encryption enum 54 | #[derive(Clone, Copy)] 55 | pub enum Encryption { 56 | /// Secp256k1 57 | Secp256k1, 58 | /// Ed25519 59 | Ed25519, 60 | /// Sm2 61 | Sm2, 62 | } 63 | 64 | impl FromStr for Encryption { 65 | type Err = String; 66 | 67 | fn from_str(s: &str) -> Result { 68 | match s.to_lowercase().as_str() { 69 | "secp256k1" => Ok(Encryption::Secp256k1), 70 | "ed25519" => Ok(Encryption::Ed25519), 71 | "sm2" => Ok(Encryption::Sm2), 72 | _ => Err("Unsupported algorithm".to_string()), 73 | } 74 | } 75 | } 76 | 77 | impl fmt::Debug for Encryption { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 79 | let msg = match self { 80 | Encryption::Secp256k1 => "secp256k1", 81 | Encryption::Ed25519 => "ed25519", 82 | Encryption::Sm2 => "sm2", 83 | }; 84 | write!(f, "{}", msg) 85 | } 86 | } 87 | 88 | impl fmt::Display for Encryption { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 90 | let msg = match self { 91 | Encryption::Secp256k1 => "secp256k1", 92 | Encryption::Ed25519 => "ed25519", 93 | Encryption::Sm2 => "sm2", 94 | }; 95 | write!(f, "{}", msg) 96 | } 97 | } 98 | 99 | /// Private key of Secp256k1/Ed25519/Sm2 100 | #[derive(Clone, Copy)] 101 | pub enum PrivateKey { 102 | /// Secp256k1 103 | Secp256k1(Secp256k1PrivKey), 104 | /// Ed25519 105 | Ed25519(Ed25519PrivKey), 106 | /// Sm2 107 | Sm2(Sm2Privkey), 108 | /// null 109 | Null, 110 | } 111 | 112 | impl PrivateKey { 113 | /// Create private key 114 | pub fn from_str(hex: &str, encryption: Encryption) -> Result { 115 | match encryption { 116 | Encryption::Secp256k1 => Ok(PrivateKey::Secp256k1( 117 | Secp256k1PrivKey::from_str(hex).map_err(|err| format!("{}", err))?, 118 | )), 119 | Encryption::Ed25519 => Ok(PrivateKey::Ed25519( 120 | Ed25519PrivKey::from_str(hex).map_err(|err| format!("{}", err))?, 121 | )), 122 | Encryption::Sm2 => Ok(PrivateKey::Sm2( 123 | Sm2Privkey::from_str(hex).map_err(|err| format!("{}", err))?, 124 | )), 125 | } 126 | } 127 | } 128 | 129 | impl fmt::Debug for PrivateKey { 130 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 131 | let msg = match *self { 132 | PrivateKey::Secp256k1(private_key) => encode(private_key.to_vec()), 133 | PrivateKey::Ed25519(private_key) => encode(private_key.to_vec()), 134 | PrivateKey::Sm2(private_key) => encode(private_key.to_vec()), 135 | PrivateKey::Null => "".to_string(), 136 | }; 137 | write!(f, "{}", msg) 138 | } 139 | } 140 | 141 | impl fmt::Display for PrivateKey { 142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 143 | let msg = match *self { 144 | PrivateKey::Secp256k1(private_key) => encode(private_key.to_vec()), 145 | PrivateKey::Ed25519(private_key) => encode(private_key.to_vec()), 146 | PrivateKey::Sm2(private_key) => encode(private_key.to_vec()), 147 | PrivateKey::Null => "".to_string(), 148 | }; 149 | write!(f, "{}", msg) 150 | } 151 | } 152 | 153 | /// Pubkey of Secp256k1/Ed25519/Sm2 154 | pub enum PubKey { 155 | /// sha3 156 | Secp256k1(Secp256k1PubKey), 157 | /// blake2b 158 | Ed25519(Ed25519PubKey), 159 | /// Sm2 160 | Sm2(Sm2Pubkey), 161 | /// null 162 | Null, 163 | } 164 | 165 | impl PubKey { 166 | /// Create pubkey key 167 | pub fn from_str(hex: &str, encryption: Encryption) -> Result { 168 | match encryption { 169 | Encryption::Secp256k1 => Ok(PubKey::Secp256k1( 170 | Secp256k1PubKey::from_str(hex).map_err(|err| format!("{}", err))?, 171 | )), 172 | Encryption::Ed25519 => Ok(PubKey::Ed25519( 173 | Ed25519PubKey::from_str(hex).map_err(|err| format!("{}", err))?, 174 | )), 175 | Encryption::Sm2 => Ok(PubKey::Sm2( 176 | Sm2Pubkey::from_str(hex).map_err(|err| format!("{}", err))?, 177 | )), 178 | } 179 | } 180 | 181 | /// Convert to vec 182 | pub fn to_vec(&self) -> Vec { 183 | match self { 184 | PubKey::Secp256k1(pk) | PubKey::Sm2(pk) => pk.to_vec(), 185 | PubKey::Ed25519(pk) => pk.to_vec(), 186 | PubKey::Null => Vec::new(), 187 | } 188 | } 189 | } 190 | 191 | impl fmt::Display for PubKey { 192 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 193 | let msg = match *self { 194 | PubKey::Secp256k1(pubkey) => encode(pubkey.to_vec()), 195 | PubKey::Ed25519(pubkey) => encode(pubkey.to_vec()), 196 | PubKey::Sm2(pubkey) => encode(pubkey.to_vec()), 197 | PubKey::Null => "".to_string(), 198 | }; 199 | write!(f, "{}", msg) 200 | } 201 | } 202 | 203 | /// key pair of Secp256k1/Ed25519/Sm2 204 | pub enum KeyPair { 205 | /// Secp256k1 206 | Secp256k1(Secp256k1KeyPair), 207 | /// Ed25519 208 | Ed25519(Ed25519KeyPair), 209 | /// Sm2 210 | Sm2(Sm2KeyPair), 211 | /// null 212 | Null, 213 | } 214 | 215 | impl KeyPair { 216 | /// Create new key pair 217 | pub fn new(encryption: Encryption) -> Self { 218 | match encryption { 219 | Encryption::Secp256k1 => KeyPair::Secp256k1(Secp256k1KeyPair::gen_keypair()), 220 | Encryption::Ed25519 => KeyPair::Ed25519(Ed25519KeyPair::gen_keypair()), 221 | Encryption::Sm2 => KeyPair::Sm2(Sm2KeyPair::gen_keypair()), 222 | } 223 | } 224 | 225 | /// New with private key 226 | pub fn from_privkey(private_key: PrivateKey) -> Self { 227 | match private_key { 228 | PrivateKey::Secp256k1(pk) => { 229 | KeyPair::Secp256k1(Secp256k1KeyPair::from_privkey(pk).unwrap()) 230 | } 231 | PrivateKey::Ed25519(pk) => KeyPair::Ed25519(Ed25519KeyPair::from_privkey(pk).unwrap()), 232 | PrivateKey::Sm2(pk) => KeyPair::Sm2(Sm2KeyPair::from_privkey(pk).unwrap()), 233 | PrivateKey::Null => KeyPair::Null, 234 | } 235 | } 236 | 237 | /// Get private key 238 | pub fn privkey(&self) -> PrivateKey { 239 | match self { 240 | KeyPair::Secp256k1(key_pair) => PrivateKey::Secp256k1(*key_pair.privkey()), 241 | KeyPair::Ed25519(key_pair) => PrivateKey::Ed25519(*key_pair.privkey()), 242 | KeyPair::Sm2(key_pair) => PrivateKey::Sm2(*key_pair.privkey()), 243 | KeyPair::Null => PrivateKey::Null, 244 | } 245 | } 246 | 247 | /// Get pubkey 248 | pub fn pubkey(&self) -> PubKey { 249 | match self { 250 | KeyPair::Secp256k1(key_pair) => PubKey::Secp256k1(*key_pair.pubkey()), 251 | KeyPair::Ed25519(key_pair) => PubKey::Ed25519(*key_pair.pubkey()), 252 | KeyPair::Sm2(key_pair) => PubKey::Sm2(*key_pair.pubkey()), 253 | KeyPair::Null => PubKey::Null, 254 | } 255 | } 256 | 257 | /// Get Address 258 | pub fn address(&self) -> Address { 259 | match self { 260 | KeyPair::Secp256k1(private_key) => private_key.address(), 261 | KeyPair::Ed25519(private_key) => private_key.address(), 262 | KeyPair::Sm2(private_key) => private_key.address(), 263 | KeyPair::Null => Address::default(), 264 | } 265 | } 266 | } 267 | 268 | impl KeyPair { 269 | /// New from private key 270 | pub fn from_str(private_key: &str, encryption: Encryption) -> Result { 271 | match PrivateKey::from_str(private_key, encryption)? { 272 | PrivateKey::Secp256k1(private) => Ok(KeyPair::Secp256k1( 273 | Secp256k1KeyPair::from_privkey(private).map_err(|err| format!("{}", err))?, 274 | )), 275 | PrivateKey::Ed25519(private) => Ok(KeyPair::Ed25519( 276 | Ed25519KeyPair::from_privkey(private).map_err(|err| format!("{}", err))?, 277 | )), 278 | PrivateKey::Sm2(private) => Ok(KeyPair::Sm2( 279 | Sm2KeyPair::from_privkey(private).map_err(|err| format!("{}", err))?, 280 | )), 281 | PrivateKey::Null => Ok(KeyPair::Null), 282 | } 283 | } 284 | } 285 | 286 | /// Signature 287 | pub enum Signature { 288 | /// Secp256k1 289 | Secp256k1(Secp256k1Signature), 290 | /// Ed25519 291 | Ed25519(Ed25519Signature), 292 | /// Sm2 293 | Sm2(Sm2Signature), 294 | /// null 295 | Null, 296 | } 297 | 298 | impl Signature { 299 | /// New from slice 300 | pub fn from(slice: &[u8]) -> Self { 301 | if slice.len() == 96 { 302 | Signature::Ed25519(Ed25519Signature::from(slice)) 303 | } else if slice.len() == 65 { 304 | Signature::Secp256k1(Secp256k1Signature::from(slice)) 305 | } else if slice.len() == 128 { 306 | Signature::Sm2(Sm2Signature::from(slice)) 307 | } else { 308 | Signature::Null 309 | } 310 | } 311 | 312 | /// Convert to vec 313 | pub fn to_vec(&self) -> Vec { 314 | match self { 315 | Signature::Sm2(sig) => sig.to_vec(), 316 | Signature::Secp256k1(sig) => sig.to_vec(), 317 | Signature::Ed25519(sig) => sig.to_vec(), 318 | Signature::Null => Vec::new(), 319 | } 320 | } 321 | 322 | /// Recover public key 323 | pub fn recover(&self, message: &Message) -> Result { 324 | match self { 325 | Signature::Secp256k1(sig) => Ok(sig 326 | .recover(message) 327 | .map(|pubkey| PubKey::from_str(&pubkey.lower_hex(), Encryption::Secp256k1).unwrap()) 328 | .map_err(|_| "Can't recover to public key".to_string())?), 329 | Signature::Ed25519(sig) => Ok(sig 330 | .recover(message) 331 | .map(|pubkey| PubKey::from_str(&pubkey.lower_hex(), Encryption::Ed25519).unwrap()) 332 | .map_err(|_| "Can't recover to public key".to_string())?), 333 | Signature::Sm2(sig) => Ok(sig 334 | .recover(message) 335 | .map(|pubkey| PubKey::from_str(&pubkey.lower_hex(), Encryption::Sm2).unwrap()) 336 | .map_err(|_| "Can't recover to public key".to_string())?), 337 | Signature::Null => Err("Mismatched encryption algorithm".to_string()), 338 | } 339 | } 340 | 341 | /// Verify public key 342 | pub fn verify_public(&self, pubkey: PubKey, message: &Message) -> Result { 343 | match (self, pubkey) { 344 | (Signature::Secp256k1(sig), PubKey::Secp256k1(pubkey)) => sig 345 | .verify_public(&pubkey, &message) 346 | .map_err(|e| e.to_string()), 347 | (Signature::Ed25519(sig), PubKey::Ed25519(pubkey)) => sig 348 | .verify_public(&pubkey, &message) 349 | .map_err(|e| e.to_string()), 350 | (Signature::Sm2(sig), PubKey::Sm2(pubkey)) => sig 351 | .verify_public(&pubkey, &message) 352 | .map_err(|e| e.to_string()), 353 | (_, _) => Ok(false), 354 | } 355 | } 356 | } 357 | 358 | impl fmt::Display for Signature { 359 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 360 | match self { 361 | Signature::Secp256k1(sig) => write!(f, "{}", encode(sig.0[..].to_vec())), 362 | Signature::Ed25519(sig) => write!(f, "{}", encode(sig.0[..].to_vec())), 363 | Signature::Sm2(sig) => write!(f, "{}", encode(sig.0[..].to_vec())), 364 | Signature::Null => write!(f, "null"), 365 | } 366 | } 367 | } 368 | 369 | #[cfg(test)] 370 | mod test { 371 | use super::{Encryption, KeyPair}; 372 | 373 | #[test] 374 | fn secp256k1_generate_from_private_key() { 375 | let key_pair = KeyPair::from_str( 376 | "8ee6aa885d9598f9c4e010b659aeecfc3f113beb646166414756568ab656f0f9", 377 | Encryption::Secp256k1, 378 | ) 379 | .unwrap(); 380 | 381 | assert_eq!( 382 | format!("{}", key_pair.pubkey()).as_str(), 383 | "e407bef7ef0a0e21395c46cc2e1ed324119783d0f4f47b676d95b23991f9065db1aa7a9099e2193160243a02168feb70c62eb8442e45c4b3542a4b3c8c8ac5bd" 384 | ); 385 | 386 | assert_eq!( 387 | format!("{:x}", key_pair.address()).as_str(), 388 | "eea5c3cbb32fec85bc9b9bffa65fc027e4b1c6d5" 389 | ); 390 | } 391 | 392 | #[test] 393 | fn sm2_generate_from_private_key() { 394 | let key_pair = KeyPair::from_str( 395 | "c3cf5004e9b025427cb07df7592ebbcc64bbf7285bbf50099f072fc0d06a2b20", 396 | Encryption::Sm2, 397 | ) 398 | .unwrap(); 399 | assert_eq!( 400 | format!("{}", key_pair.pubkey()).as_str(), 401 | "c82d3230f65335a4d07f81d5ab014c1bb606c90b2d098dadbe0bf1d9cf4618654b3a1310627703859ecf493055ea8389fcb78d9c3cf372780927e076278603ed" 402 | ); 403 | 404 | assert_eq!( 405 | format!("{:x}", key_pair.address()).as_str(), 406 | "f73076eed94014142153a9556a810826ba9ae857" 407 | ); 408 | } 409 | 410 | #[test] 411 | fn ed25519_generate_from_private_key() { 412 | let key_pair = 413 | KeyPair::from_str( 414 | "87c8f34545181d38666aadaeee4924e811263e05f6e2d87d75fac27ab5075915456fdf394a9c4397ec29f1a72c16d601b4ee7f08160c784877cb6941a0e177a1", 415 | Encryption::Ed25519 416 | ).unwrap(); 417 | 418 | assert_eq!( 419 | format!("{}", key_pair.pubkey()).as_str(), 420 | "456fdf394a9c4397ec29f1a72c16d601b4ee7f08160c784877cb6941a0e177a1" 421 | ); 422 | 423 | assert_eq!( 424 | format!("{:x}", key_pair.address()).as_str(), 425 | "5ae200f77d5c7df715f6ccb182fc5073dab1cfe9" 426 | ); 427 | } 428 | } 429 | -------------------------------------------------------------------------------- /cita-tool/src/crypto/cita_ed25519.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{ 2 | pubkey_to_address, CreateKey, Ed25519PrivKey, Ed25519PubKey, Error, Message, PubKey, 3 | }; 4 | use ed25519_dalek::{ 5 | Keypair, PublicKey as EdPublicKey, SecretKey as EdSecretKey, Signature as EdSignature, 6 | }; 7 | use hex::encode; 8 | use rand::thread_rng; 9 | use sha2::Sha512; 10 | use std::fmt; 11 | use std::ops::{Deref, DerefMut}; 12 | use types::Address; 13 | 14 | const SIGNATURE_BYTES_LEN: usize = 96; 15 | 16 | /// Ed25519 key pair 17 | #[derive(Default)] 18 | pub struct Ed25519KeyPair { 19 | privkey: Ed25519PrivKey, 20 | pubkey: Ed25519PubKey, 21 | } 22 | 23 | impl fmt::Display for Ed25519KeyPair { 24 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 25 | writeln!(f, "privkey: {}", encode(self.privkey.0.to_vec()))?; 26 | writeln!(f, "pubkey: {}", encode(self.pubkey.0.to_vec()))?; 27 | write!(f, "address: {}", encode(self.address().0.to_vec())) 28 | } 29 | } 30 | 31 | impl CreateKey for Ed25519KeyPair { 32 | type PrivKey = Ed25519PrivKey; 33 | type PubKey = Ed25519PubKey; 34 | type Error = Error; 35 | 36 | fn from_privkey(privkey: Self::PrivKey) -> Result { 37 | let pubkey = Ed25519PubKey::from(&privkey.0[32..]); 38 | Ok(Ed25519KeyPair { privkey, pubkey }) 39 | } 40 | 41 | fn gen_keypair() -> Self { 42 | let keypair = Keypair::generate::(&mut thread_rng()); 43 | let pubkey = keypair.public.to_bytes(); 44 | let mut privkey = [0u8; 64]; 45 | privkey[..32].copy_from_slice(&keypair.secret.to_bytes()); 46 | privkey[32..].copy_from_slice(&pubkey); 47 | 48 | Ed25519KeyPair { 49 | privkey: Ed25519PrivKey::from(privkey), 50 | pubkey: Ed25519PubKey::from(pubkey), 51 | } 52 | } 53 | 54 | fn privkey(&self) -> &Self::PrivKey { 55 | &self.privkey 56 | } 57 | 58 | fn pubkey(&self) -> &Self::PubKey { 59 | &self.pubkey 60 | } 61 | 62 | fn address(&self) -> Address { 63 | pubkey_to_address(&PubKey::Ed25519(self.pubkey)) 64 | } 65 | } 66 | 67 | /// Ed25519 signature 68 | pub struct Ed25519Signature(pub [u8; 96]); 69 | 70 | impl Ed25519Signature { 71 | /// sign area 0-64 72 | pub fn sig(&self) -> &[u8] { 73 | &self.0[0..64] 74 | } 75 | 76 | /// pub area 64-96 77 | pub fn pk(&self) -> &[u8] { 78 | &self.0[64..96] 79 | } 80 | 81 | /// Recover public key 82 | pub fn recover(&self, message: &Message) -> Result { 83 | let sig = self.sig(); 84 | let pubkey = self.pk(); 85 | EdPublicKey::from_bytes(&pubkey) 86 | .unwrap() 87 | .verify::(message, &EdSignature::from_bytes(&sig).unwrap()) 88 | .map_err(|_| Error::InvalidSignature) 89 | .map(|_| Ed25519PubKey::from(pubkey)) 90 | } 91 | 92 | /// Verify public key 93 | pub fn verify_public(&self, pubkey: &Ed25519PubKey, message: &Message) -> Result { 94 | let sig = self.sig(); 95 | let pk = self.pk(); 96 | if pk != pubkey.as_ref() as &[u8] { 97 | return Err(Error::InvalidPubKey); 98 | } 99 | 100 | EdPublicKey::from_bytes(&pubkey) 101 | .unwrap() 102 | .verify::(message.as_ref(), &EdSignature::from_bytes(&sig).unwrap()) 103 | .map_err(|_| Error::InvalidSignature) 104 | .map(|_| true) 105 | } 106 | } 107 | 108 | impl PartialEq for Ed25519Signature { 109 | fn eq(&self, rhs: &Self) -> bool { 110 | self.0[..] == rhs.0[..] 111 | } 112 | } 113 | 114 | impl Deref for Ed25519Signature { 115 | type Target = [u8; 96]; 116 | 117 | fn deref(&self) -> &Self::Target { 118 | &self.0 119 | } 120 | } 121 | 122 | impl DerefMut for Ed25519Signature { 123 | fn deref_mut(&mut self) -> &mut Self::Target { 124 | &mut self.0 125 | } 126 | } 127 | 128 | impl From<[u8; 96]> for Ed25519Signature { 129 | fn from(bytes: [u8; 96]) -> Self { 130 | Ed25519Signature(bytes) 131 | } 132 | } 133 | 134 | impl<'a> From<&'a [u8]> for Ed25519Signature { 135 | fn from(slice: &'a [u8]) -> Ed25519Signature { 136 | assert_eq!(slice.len(), SIGNATURE_BYTES_LEN); 137 | let mut bytes = [0u8; 96]; 138 | bytes.copy_from_slice(&slice[..]); 139 | Ed25519Signature(bytes) 140 | } 141 | } 142 | 143 | impl fmt::Display for Ed25519Signature { 144 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 145 | write!(f, "{}", encode(self.0[..].to_vec())) 146 | } 147 | } 148 | 149 | /// Sign data with ed25519 150 | pub fn ed25519_sign( 151 | privkey: &Ed25519PrivKey, 152 | message: &Message, 153 | ) -> Result { 154 | let secret = EdSecretKey::from_bytes(&privkey.0[..32]).unwrap(); 155 | let public = EdPublicKey::from_bytes(&privkey.0[32..]).unwrap(); 156 | let keypair = Keypair { secret, public }; 157 | let sig = keypair.sign::(message); 158 | 159 | let mut ret = [0u8; 96]; 160 | ret[0..64].copy_from_slice(&sig.to_bytes()); 161 | ret[64..96].copy_from_slice(public.as_bytes()); 162 | Ok(Ed25519Signature(ret)) 163 | } 164 | 165 | #[cfg(test)] 166 | mod tests { 167 | use super::*; 168 | 169 | #[test] 170 | fn test_recover() { 171 | let keypair = Ed25519KeyPair::gen_keypair(); 172 | let msg = Message::default(); 173 | let sig = ed25519_sign(keypair.privkey(), &msg).unwrap(); 174 | assert_eq!(keypair.pubkey(), &sig.recover(&msg).unwrap()); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /cita-tool/src/crypto/cita_secp256k1.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{ 2 | pubkey_to_address, CreateKey, Error, Message, PubKey, Secp256k1PrivKey, Secp256k1PubKey, 3 | }; 4 | use hex::encode; 5 | use lazy_static::lazy_static; 6 | use secp256k1::{ 7 | constants, 8 | key::{self, PublicKey, SecretKey}, 9 | rand::rngs::OsRng, 10 | recovery::{RecoverableSignature, RecoveryId}, 11 | Error as SecpError, Message as SecpMessage, Secp256k1, 12 | }; 13 | use std::fmt; 14 | use std::ops::{Deref, DerefMut}; 15 | use types::{Address, H256}; 16 | 17 | lazy_static! { 18 | pub static ref SECP256K1: Secp256k1 = Secp256k1::new(); 19 | } 20 | 21 | const SIGNATURE_BYTES_LEN: usize = 65; 22 | 23 | /// Secp256k1 key pair 24 | #[derive(Default)] 25 | pub struct Secp256k1KeyPair { 26 | privkey: Secp256k1PrivKey, 27 | pubkey: Secp256k1PubKey, 28 | } 29 | 30 | impl fmt::Display for Secp256k1KeyPair { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 32 | writeln!(f, "privkey: {}", encode(self.privkey.0.to_vec()))?; 33 | writeln!(f, "pubkey: {}", encode(self.pubkey.0.to_vec()))?; 34 | write!(f, "address: {}", encode(self.address().0.to_vec())) 35 | } 36 | } 37 | 38 | impl CreateKey for Secp256k1KeyPair { 39 | type PrivKey = Secp256k1PrivKey; 40 | type PubKey = Secp256k1PubKey; 41 | type Error = Error; 42 | 43 | /// Create a pair from secret key 44 | fn from_privkey(privkey: Self::PrivKey) -> Result { 45 | let context = &SECP256K1; 46 | let s: key::SecretKey = key::SecretKey::from_slice(&privkey.0[..])?; 47 | let pubkey = key::PublicKey::from_secret_key(context, &s); 48 | let serialized = pubkey.serialize_uncompressed(); 49 | 50 | let mut pubkey = Secp256k1PubKey::default(); 51 | pubkey 52 | .0 53 | .copy_from_slice(&serialized[1..constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]); 54 | 55 | let keypair = Secp256k1KeyPair { privkey, pubkey }; 56 | 57 | Ok(keypair) 58 | } 59 | 60 | fn gen_keypair() -> Self { 61 | let context = &SECP256K1; 62 | let (s, p) = context.generate_keypair(&mut OsRng::new().unwrap()); 63 | let serialized = p.serialize_uncompressed(); 64 | let mut privkey = Secp256k1PrivKey::default(); 65 | privkey.0.copy_from_slice(&s[0..32]); 66 | let mut pubkey = Secp256k1PubKey::default(); 67 | pubkey 68 | .0 69 | .copy_from_slice(&serialized[1..constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]); 70 | Secp256k1KeyPair { privkey, pubkey } 71 | } 72 | 73 | fn privkey(&self) -> &Self::PrivKey { 74 | &self.privkey 75 | } 76 | 77 | fn pubkey(&self) -> &Self::PubKey { 78 | &self.pubkey 79 | } 80 | 81 | fn address(&self) -> Address { 82 | pubkey_to_address(&PubKey::Secp256k1(self.pubkey)) 83 | } 84 | } 85 | 86 | /// Signature 87 | pub struct Secp256k1Signature(pub [u8; 65]); 88 | 89 | impl Secp256k1Signature { 90 | /// Get a slice into the 'r' portion of the data. 91 | pub fn r(&self) -> &[u8] { 92 | &self.0[0..32] 93 | } 94 | 95 | /// Get a slice into the 's' portion of the data. 96 | pub fn s(&self) -> &[u8] { 97 | &self.0[32..64] 98 | } 99 | 100 | /// Get the recovery byte. 101 | pub fn v(&self) -> u8 { 102 | self.0[64] 103 | } 104 | 105 | /// Create a signature object from the sig. 106 | pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Secp256k1Signature { 107 | let mut sig = [0u8; 65]; 108 | sig[0..32].copy_from_slice(&r.0); 109 | sig[32..64].copy_from_slice(&s.0); 110 | sig[64] = v; 111 | Secp256k1Signature(sig) 112 | } 113 | 114 | /// Check if this is a "low" signature. 115 | pub fn is_low_s(&self) -> bool { 116 | H256::from(self.s()) 117 | <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() 118 | } 119 | 120 | /// Check if each component of the signature is in range. 121 | pub fn is_valid(&self) -> bool { 122 | self.v() <= 1 123 | && H256::from(self.r()) 124 | < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() 125 | && H256::from(self.r()) >= 1.into() 126 | && H256::from(self.s()) 127 | < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() 128 | && H256::from(self.s()) >= 1.into() 129 | } 130 | 131 | /// Recover public key 132 | pub fn recover(&self, message: &Message) -> Result { 133 | let context = &SECP256K1; 134 | let rsig = RecoverableSignature::from_compact( 135 | &self.0[0..64], 136 | RecoveryId::from_i32(i32::from(self.0[64] as i8))?, 137 | )?; 138 | let public = context.recover(&SecpMessage::from_slice(&message.0[..])?, &rsig)?; 139 | let serialized = public.serialize_uncompressed(); 140 | 141 | let mut pubkey = Secp256k1PubKey::default(); 142 | pubkey 143 | .0 144 | .copy_from_slice(&serialized[1..constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]); 145 | Ok(pubkey) 146 | } 147 | 148 | /// Verify public key 149 | pub fn verify_public( 150 | &self, 151 | pubkey: &Secp256k1PubKey, 152 | message: &Message, 153 | ) -> Result { 154 | let context = &SECP256K1; 155 | let rsig = RecoverableSignature::from_compact( 156 | &self.0[0..64], 157 | RecoveryId::from_i32(i32::from(self.0[64]))?, 158 | )?; 159 | let sig = rsig.to_standard(); 160 | 161 | let pdata: [u8; 65] = { 162 | let mut temp = [4u8; 65]; 163 | temp[1..65].copy_from_slice(pubkey); 164 | temp 165 | }; 166 | 167 | let publ = PublicKey::from_slice(&pdata)?; 168 | match context.verify(&SecpMessage::from_slice(&message.0[..])?, &sig, &publ) { 169 | Ok(_) => Ok(true), 170 | Err(SecpError::IncorrectSignature) => Ok(false), 171 | Err(x) => Err(Error::from(x)), 172 | } 173 | } 174 | } 175 | 176 | /// Sign data with secp256k1 177 | pub fn secp256k1_sign( 178 | privkey: &Secp256k1PrivKey, 179 | message: &Message, 180 | ) -> Result { 181 | let context = &SECP256K1; 182 | // no way to create from raw byte array. 183 | let sec: &SecretKey = unsafe { &*(privkey as *const H256 as *const SecretKey) }; 184 | let s = context.sign_recoverable(&SecpMessage::from_slice(&message.0[..])?, sec); 185 | let (rec_id, data) = s.serialize_compact(); 186 | let mut data_arr = [0; 65]; 187 | 188 | // no need to check if s is low, it always is 189 | data_arr[0..64].copy_from_slice(&data[0..64]); 190 | data_arr[64] = rec_id.to_i32() as u8; 191 | Ok(Secp256k1Signature(data_arr)) 192 | } 193 | 194 | impl Deref for Secp256k1Signature { 195 | type Target = [u8; 65]; 196 | 197 | fn deref(&self) -> &Self::Target { 198 | &self.0 199 | } 200 | } 201 | 202 | impl DerefMut for Secp256k1Signature { 203 | fn deref_mut(&mut self) -> &mut Self::Target { 204 | &mut self.0 205 | } 206 | } 207 | 208 | impl PartialEq for Secp256k1Signature { 209 | fn eq(&self, rhs: &Self) -> bool { 210 | self.0[..] == rhs.0[..] 211 | } 212 | } 213 | 214 | impl Eq for Secp256k1Signature {} 215 | 216 | impl fmt::Debug for Secp256k1Signature { 217 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 218 | f.debug_struct("Signature") 219 | .field("r", &encode(self.0[0..32].to_vec())) 220 | .field("s", &encode(self.0[32..64].to_vec())) 221 | .field("v", &encode(self.0[64..65].to_vec())) 222 | .finish() 223 | } 224 | } 225 | 226 | impl fmt::Display for Secp256k1Signature { 227 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 228 | write!(f, "{}", encode(self.to_vec())) 229 | } 230 | } 231 | 232 | impl Default for Secp256k1Signature { 233 | fn default() -> Self { 234 | Secp256k1Signature([0; 65]) 235 | } 236 | } 237 | 238 | impl<'a> From<&'a [u8]> for Secp256k1Signature { 239 | fn from(slice: &'a [u8]) -> Secp256k1Signature { 240 | assert_eq!(slice.len(), SIGNATURE_BYTES_LEN); 241 | let mut bytes = [0u8; 65]; 242 | bytes.copy_from_slice(&slice[..]); 243 | Secp256k1Signature(bytes) 244 | } 245 | } 246 | 247 | #[cfg(test)] 248 | mod tests { 249 | use super::*; 250 | 251 | #[test] 252 | fn test_recover() { 253 | let keypair = Secp256k1KeyPair::gen_keypair(); 254 | let msg = Message::from([1; 32]); 255 | let sig = secp256k1_sign(keypair.privkey(), &msg).unwrap(); 256 | assert_eq!(keypair.pubkey(), &sig.recover(&msg).unwrap()); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /cita-tool/src/crypto/cita_sm2.rs: -------------------------------------------------------------------------------- 1 | use hex::encode; 2 | use libsm::sm2::signature::{SigCtx, Signature as RawSignature}; 3 | use std::fmt; 4 | use std::ops::{Deref, DerefMut}; 5 | use types::Address; 6 | 7 | use crate::crypto::{pubkey_to_address, CreateKey, Error, Message, PubKey, Sm2Privkey, Sm2Pubkey}; 8 | 9 | const SIGNATURE_BYTES_LEN: usize = 128; 10 | 11 | /// Sm2 key pair 12 | pub struct Sm2KeyPair { 13 | privkey: Sm2Privkey, 14 | pubkey: Sm2Pubkey, 15 | } 16 | 17 | impl fmt::Display for Sm2KeyPair { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 19 | writeln!(f, "privkey: {}", encode(self.privkey.0.to_vec()))?; 20 | writeln!(f, "pubkey: {}", encode(self.pubkey.0.to_vec()))?; 21 | write!(f, "address: {}", encode(self.address().0.to_vec())) 22 | } 23 | } 24 | 25 | impl CreateKey for Sm2KeyPair { 26 | type PrivKey = Sm2Privkey; 27 | type PubKey = Sm2Pubkey; 28 | type Error = Error; 29 | 30 | fn from_privkey(privkey: Self::PrivKey) -> Result { 31 | let ctx = SigCtx::new(); 32 | ctx.load_seckey(&privkey.0) 33 | .map_err(|_| Error::RecoverError) 34 | .map(|sk| { 35 | let pk = ctx.pk_from_sk(&sk); 36 | let pubkey = Sm2Pubkey::from(&ctx.serialize_pubkey(&pk, false)[1..]); 37 | Sm2KeyPair { privkey, pubkey } 38 | }) 39 | } 40 | 41 | fn gen_keypair() -> Self { 42 | let ctx = SigCtx::new(); 43 | let (pk, sk) = ctx.new_keypair(); 44 | let pubkey = Sm2Pubkey::from(&ctx.serialize_pubkey(&pk, false)[1..]); 45 | let privkey = Sm2Privkey::from(&ctx.serialize_seckey(&sk)[..]); 46 | Sm2KeyPair { privkey, pubkey } 47 | } 48 | 49 | fn privkey(&self) -> &Self::PrivKey { 50 | &self.privkey 51 | } 52 | 53 | fn pubkey(&self) -> &Self::PubKey { 54 | &self.pubkey 55 | } 56 | 57 | fn address(&self) -> Address { 58 | pubkey_to_address(&PubKey::Sm2(self.pubkey)) 59 | } 60 | } 61 | 62 | /// Sm2 signature 63 | pub struct Sm2Signature(pub [u8; 128]); 64 | 65 | impl Sm2Signature { 66 | #[inline] 67 | fn r(&self) -> &[u8] { 68 | &self.0[0..32] 69 | } 70 | 71 | #[inline] 72 | fn s(&self) -> &[u8] { 73 | &self.0[32..64] 74 | } 75 | 76 | #[inline] 77 | fn pk(&self) -> &[u8] { 78 | &self.0[64..] 79 | } 80 | 81 | /// Recover public key 82 | pub fn recover(&self, message: &Message) -> Result { 83 | let ctx = SigCtx::new(); 84 | let sig = RawSignature::new(self.r(), self.s()); 85 | let mut pk_full = [0u8; 65]; 86 | pk_full[0] = 4; 87 | pk_full[1..].copy_from_slice(self.pk()); 88 | ctx.load_pubkey(&pk_full[..]) 89 | .map_err(|_| Error::RecoverError) 90 | .and_then(|pk| { 91 | if ctx.verify(&message, &pk, &sig) { 92 | Ok(Sm2Pubkey::from(self.pk())) 93 | } else { 94 | Err(Error::RecoverError) 95 | } 96 | }) 97 | } 98 | 99 | /// Verify public key 100 | pub fn verify_public(&self, pubkey: &Sm2Pubkey, message: &Message) -> Result { 101 | let pubkey_from_sig = Sm2Pubkey::from(self.pk()); 102 | if pubkey_from_sig == *pubkey { 103 | let ctx = SigCtx::new(); 104 | let sig = RawSignature::new(self.r(), self.s()); 105 | let mut pk_full = [0u8; 65]; 106 | pk_full[0] = 4; 107 | pk_full[1..].copy_from_slice(self.pk()); 108 | ctx.load_pubkey(&pk_full[..]) 109 | .map_err(|_| Error::RecoverError) 110 | .map(|pk| ctx.verify(&message, &pk, &sig)) 111 | } else { 112 | Ok(false) 113 | } 114 | } 115 | } 116 | 117 | /// Sign data with sm2 118 | pub fn sm2_sign(privkey: &Sm2Privkey, message: &Message) -> Result { 119 | let ctx = SigCtx::new(); 120 | ctx.load_seckey(&privkey.0) 121 | .map_err(|_| Error::RecoverError) 122 | .map(|sk| { 123 | let pk = ctx.pk_from_sk(&sk); 124 | let signature = ctx.sign(&message, &sk, &pk); 125 | let mut sig_bytes = [0u8; SIGNATURE_BYTES_LEN]; 126 | let r_bytes = signature.get_r().to_bytes_be(); 127 | let s_bytes = signature.get_s().to_bytes_be(); 128 | sig_bytes[32 - r_bytes.len()..32].copy_from_slice(&r_bytes[..]); 129 | sig_bytes[64 - s_bytes.len()..64].copy_from_slice(&s_bytes[..]); 130 | sig_bytes[64..].copy_from_slice(&ctx.serialize_pubkey(&pk, false)[1..]); 131 | sig_bytes.into() 132 | }) 133 | } 134 | 135 | impl fmt::Debug for Sm2Signature { 136 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 137 | f.debug_struct("Signature") 138 | .field("r", &encode(&self.r())) 139 | .field("s", &encode(&self.s())) 140 | .field("pk", &encode(&self.pk())) 141 | .finish() 142 | } 143 | } 144 | 145 | impl fmt::Display for Sm2Signature { 146 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 147 | write!(f, "{}", encode(self.0[..].to_vec())) 148 | } 149 | } 150 | 151 | impl Default for Sm2Signature { 152 | fn default() -> Self { 153 | Sm2Signature([0; 128]) 154 | } 155 | } 156 | 157 | impl From<[u8; 128]> for Sm2Signature { 158 | fn from(s: [u8; 128]) -> Self { 159 | Sm2Signature(s) 160 | } 161 | } 162 | 163 | impl<'a> From<&'a [u8]> for Sm2Signature { 164 | fn from(slice: &'a [u8]) -> Sm2Signature { 165 | assert_eq!(slice.len(), SIGNATURE_BYTES_LEN); 166 | let mut bytes = [0u8; SIGNATURE_BYTES_LEN]; 167 | bytes.copy_from_slice(&slice[..]); 168 | Sm2Signature(bytes) 169 | } 170 | } 171 | 172 | impl fmt::LowerHex for Sm2Signature { 173 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 174 | for i in &self.0[..] { 175 | write!(f, "{:02x}", i)?; 176 | } 177 | Ok(()) 178 | } 179 | } 180 | 181 | impl Deref for Sm2Signature { 182 | type Target = [u8; 128]; 183 | 184 | fn deref(&self) -> &Self::Target { 185 | &self.0 186 | } 187 | } 188 | 189 | impl DerefMut for Sm2Signature { 190 | fn deref_mut(&mut self) -> &mut Self::Target { 191 | &mut self.0 192 | } 193 | } 194 | 195 | #[cfg(test)] 196 | mod tests { 197 | use super::*; 198 | 199 | #[test] 200 | fn test_recover() { 201 | let keypair = Sm2KeyPair::gen_keypair(); 202 | let msg = Message::default(); 203 | let sig = sm2_sign(keypair.privkey(), &msg).unwrap(); 204 | assert_eq!(keypair.pubkey(), &sig.recover(&msg).unwrap()); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /cita-tool/src/crypto/crypto_trait.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::Encryption; 2 | use blake2b_simd::Params; 3 | use libsm::sm3; 4 | use std::{fmt, marker}; 5 | use tiny_keccak; 6 | use types::{Address, H256}; 7 | 8 | const BLAKE2BKEY: &str = "CryptapeCryptape"; 9 | 10 | /// Create secret Key 11 | pub trait CreateKey 12 | where 13 | Self: marker::Sized, 14 | { 15 | /// Private key 16 | type PrivKey; 17 | /// Public key 18 | type PubKey; 19 | /// Error 20 | type Error; 21 | 22 | /// Create a pair from secret key 23 | fn from_privkey(privkey: Self::PrivKey) -> Result; 24 | /// Generate a pair of public and private keys 25 | fn gen_keypair() -> Self; 26 | /// Get private key 27 | fn privkey(&self) -> &Self::PrivKey; 28 | /// Get public key 29 | fn pubkey(&self) -> &Self::PubKey; 30 | /// Get address of the public key 31 | fn address(&self) -> Address; 32 | } 33 | 34 | /// Hashable for some type 35 | pub trait Hashable { 36 | /// Calculate crypt HASH of this object. 37 | fn crypt_hash(&self, encryption: Encryption) -> H256 { 38 | let mut result = [0u8; 32]; 39 | match encryption { 40 | Encryption::Secp256k1 => self.sha3_crypt_hash_into(&mut result), 41 | Encryption::Ed25519 => self.blake2b_crypt_hash_into(&mut result), 42 | Encryption::Sm2 => self.sm3_crypt_hash_into(&mut result), 43 | } 44 | H256(result) 45 | } 46 | 47 | /// Calculate crypt HASH of this object and place result into dest, use sha3 48 | fn sha3_crypt_hash_into(&self, dest: &mut [u8]); 49 | 50 | /// Calculate crypt HASH of this object and place result into dest, use blake2b 51 | fn blake2b_crypt_hash_into(&self, dest: &mut [u8]); 52 | 53 | /// Calculate crypt HASH of this object and place result into dest, use sm3 54 | fn sm3_crypt_hash_into(&self, dest: &mut [u8]); 55 | } 56 | 57 | impl Hashable for T 58 | where 59 | T: AsRef<[u8]>, 60 | { 61 | fn sha3_crypt_hash_into(&self, dest: &mut [u8]) { 62 | let input: &[u8] = self.as_ref(); 63 | tiny_keccak::Keccak::keccak256(input, dest); 64 | } 65 | 66 | fn blake2b_crypt_hash_into(&self, dest: &mut [u8]) { 67 | let input: &[u8] = self.as_ref(); 68 | 69 | dest.copy_from_slice( 70 | Params::new() 71 | .hash_length(dest.len()) 72 | .key(BLAKE2BKEY.as_bytes()) 73 | .to_state() 74 | .update(input) 75 | .finalize() 76 | .as_ref(), 77 | ); 78 | } 79 | 80 | fn sm3_crypt_hash_into(&self, dest: &mut [u8]) { 81 | let input: &[u8] = self.as_ref(); 82 | dest.copy_from_slice(sm3::hash::Sm3Hash::new(input).get_hash().as_ref()); 83 | } 84 | } 85 | 86 | /// Error of create secret key 87 | #[derive(Debug)] 88 | pub enum Error { 89 | /// Invalid private key 90 | InvalidPrivKey, 91 | /// Invalid public key 92 | InvalidPubKey, 93 | /// Invalid signature 94 | InvalidSignature, 95 | /// Invalid message 96 | InvalidMessage, 97 | /// Recover error 98 | RecoverError, 99 | /// Io error 100 | Io(::std::io::Error), 101 | } 102 | 103 | impl fmt::Display for Error { 104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 105 | let msg = match *self { 106 | Error::InvalidPrivKey => "Invalid secret".into(), 107 | Error::InvalidPubKey => "Invalid public".into(), 108 | Error::InvalidSignature => "Invalid EC signature".into(), 109 | Error::InvalidMessage => "Invalid AES message".into(), 110 | Error::RecoverError => "Recover Error".into(), 111 | Error::Io(ref err) => format!("I/O error: {}", err), 112 | }; 113 | f.write_fmt(format_args!("Crypto error ({})", msg)) 114 | } 115 | } 116 | 117 | impl From<::secp256k1::Error> for Error { 118 | fn from(e: ::secp256k1::Error) -> Error { 119 | match e { 120 | ::secp256k1::Error::InvalidMessage => Error::InvalidMessage, 121 | ::secp256k1::Error::InvalidPublicKey => Error::InvalidPubKey, 122 | ::secp256k1::Error::InvalidSecretKey => Error::InvalidPrivKey, 123 | _ => Error::InvalidSignature, 124 | } 125 | } 126 | } 127 | 128 | impl From<::std::io::Error> for Error { 129 | fn from(err: ::std::io::Error) -> Error { 130 | Error::Io(err) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /cita-tool/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | use hex::FromHexError; 3 | use hyper; 4 | use protobuf::error::ProtobufError; 5 | use serde_json; 6 | use std::num::ParseIntError; 7 | 8 | /// Error summary information 9 | #[derive(Debug, Fail)] 10 | pub enum ToolError { 11 | /// IO error 12 | #[fail(display = "Std's io error: {}", _0)] 13 | Stdio(::std::io::Error), 14 | /// Parsing json data error 15 | #[fail(display = "Serde_json error: {}", _0)] 16 | SerdeJson(serde_json::error::Error), 17 | /// Hyper error 18 | #[fail(display = "Hyper error: {}", _0)] 19 | Hyper(hyper::Error), 20 | /// ABI error 21 | #[fail(display = "ABI error: {}", _0)] 22 | Abi(String), 23 | /// Protobuf error 24 | #[fail(display = "Protobuf error: {}", _0)] 25 | Proto(ProtobufError), 26 | /// Hex decode error 27 | #[fail(display = "Hex decode error: {}", _0)] 28 | Decode(FromHexError), 29 | /// Parse error 30 | #[fail(display = "Parse int error: {}", _0)] 31 | Parse(ParseIntError), 32 | /// Customize error 33 | #[fail(display = "Customize error: {}", _0)] 34 | Customize(String), 35 | } 36 | -------------------------------------------------------------------------------- /cita-tool/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A easy-use CITA command line tool 2 | 3 | #![deny(warnings)] 4 | #![deny(missing_docs)] 5 | 6 | #[macro_use] 7 | extern crate serde_derive; 8 | 9 | /// Ethabi 10 | mod abi; 11 | /// The Jsonrpc Client 12 | pub mod client; 13 | /// Encryption algorithm library 14 | pub mod crypto; 15 | /// Error of cita tool 16 | pub mod error; 17 | /// Transaction protobuf code 18 | pub mod protos; 19 | /// Request and Response type 20 | pub mod rpctypes; 21 | 22 | pub use crate::abi::{decode_input, decode_logs, decode_params, encode_input, encode_params}; 23 | pub use crate::client::{parse_url, remove_0x, TransactionOptions}; 24 | pub use crate::crypto::{ 25 | ed25519_sign, Ed25519KeyPair, Ed25519PrivKey, Ed25519PubKey, Ed25519Signature, 26 | }; 27 | pub use crate::crypto::{ 28 | pubkey_to_address, secp256k1_sign, sign, sm2_sign, CreateKey, Encryption, Hashable, KeyPair, 29 | Message, PrivateKey, PubKey, Secp256k1KeyPair, Secp256k1PrivKey, Secp256k1PubKey, Signature, 30 | Sm2KeyPair, Sm2Privkey, Sm2Pubkey, Sm2Signature, 31 | }; 32 | pub use crate::error::ToolError; 33 | pub use crate::protos::{Crypto, SignedTransaction, Transaction, UnverifiedTransaction}; 34 | pub use crate::rpctypes::{JsonRpcParams, JsonRpcResponse, ParamsValue, ResponseValue}; 35 | pub use hex::{decode, encode}; 36 | pub use protobuf::Message as ProtoMessage; 37 | pub use types::{Address, H128, H160, H256, H264, H32, H512, H520, H64}; 38 | pub use types::{U256, U512, U64}; 39 | 40 | /// Format types 41 | pub trait LowerHex { 42 | /// hex doesn't with 0x 43 | fn lower_hex(&self) -> String; 44 | /// completed hex doesn't with 0x 45 | fn completed_lower_hex(&self) -> String; 46 | /// completed with 0x 47 | fn completed_lower_hex_with_0x(&self) -> String; 48 | /// hex with 0x 49 | fn lower_hex_with_0x(&self) -> String; 50 | } 51 | 52 | macro_rules! add_funcs { 53 | ([$( ($name:ident) ),+ ,]) => { 54 | add_funcs!([ $( ($name) ),+ ]); 55 | }; 56 | 57 | ([$( ($name:ident) ),+]) => { 58 | $( add_funcs!($name); )+ 59 | }; 60 | 61 | ($name:ident) => { 62 | impl LowerHex for $name { 63 | #[inline] 64 | fn lower_hex(&self) -> String { 65 | format!("{:x}", self) 66 | } 67 | 68 | #[inline] 69 | fn completed_lower_hex(&self) -> String { 70 | let len = stringify!($name)[1..].parse::().unwrap() / 4; 71 | format!("{:0>width$}", self.lower_hex(), width=len) 72 | } 73 | 74 | fn completed_lower_hex_with_0x(&self) -> String { 75 | let len = stringify!($name)[1..].parse::().unwrap() / 4; 76 | format!("0x{:0>width$}", self.lower_hex(), width=len) 77 | } 78 | 79 | #[inline] 80 | fn lower_hex_with_0x(&self) -> String { 81 | format!("{:#x}", self) 82 | } 83 | } 84 | } 85 | } 86 | 87 | add_funcs!([ 88 | (H32), 89 | (H64), 90 | (H128), 91 | (H160), 92 | (H256), 93 | (H264), 94 | (H512), 95 | (H520), 96 | (U64), 97 | (U256), 98 | (U512), 99 | ]); 100 | -------------------------------------------------------------------------------- /cita-tool/src/protos.rs: -------------------------------------------------------------------------------- 1 | #![allow(bare_trait_objects)] 2 | pub mod blockchain; 3 | 4 | pub use self::blockchain::{Crypto, SignedTransaction, Transaction, UnverifiedTransaction}; 5 | use crate::client::remove_0x; 6 | use crate::crypto::PubKey; 7 | use crate::crypto::{ 8 | pubkey_to_address, sign, Encryption, Hashable, KeyPair, PrivateKey, Signature, 9 | }; 10 | use crate::LowerHex; 11 | use hex; 12 | use protobuf::Message as MessageTrait; 13 | use protobuf::{parse_from_bytes, ProtobufEnum}; 14 | use serde_json::{json, Value}; 15 | use std::convert::From; 16 | use types::{Address, H256, U256}; 17 | 18 | use crate::error::ToolError; 19 | use std::str::FromStr; 20 | 21 | impl UnverifiedTransaction { 22 | /// UnverifiedTransaction as JSON Value 23 | pub fn to_json(&self, encryption: Encryption) -> Result { 24 | let tx = match self.transaction.as_ref() { 25 | Some(tx) => tx, 26 | None => return Err("Bad Transaction".to_string()), 27 | }; 28 | let pub_key = self.public_key(encryption)?; 29 | Ok(json!({ 30 | "transaction": { 31 | "to": tx.to, 32 | "to_v1": Address::from(tx.to_v1.as_slice()).completed_lower_hex_with_0x(), 33 | "nonce": tx.nonce, 34 | "quota": tx.quota, 35 | "valid_until_block": tx.valid_until_block, 36 | "data": format!("0x{}", hex::encode(&tx.data)), 37 | "value": U256::from(tx.value.as_slice()).completed_lower_hex_with_0x(), 38 | "chain_id": tx.chain_id, 39 | "chain_id_v1": U256::from(tx.chain_id_v1.as_slice()).completed_lower_hex_with_0x(), 40 | "version": tx.version, 41 | "pub_key": format!("0x{}", pub_key), 42 | "sender": pubkey_to_address(&pub_key), 43 | "encrypted_hash": tx.write_to_bytes().map_err(|e| e.to_string())?.crypt_hash(encryption) 44 | }, 45 | "signature": format!("0x{}", hex::encode(&self.signature)), 46 | "crypto": self.crypto.value(), 47 | })) 48 | } 49 | 50 | /// Get the transaction public key 51 | pub fn public_key(&self, encryption: Encryption) -> Result { 52 | let bytes: Vec = self 53 | .get_transaction() 54 | .write_to_bytes() 55 | .map_err(|e| e.to_string())?; 56 | let hash = bytes.crypt_hash(encryption); 57 | let signature = self.get_signature(); 58 | let sig = Signature::from(signature); 59 | 60 | match self.get_crypto() { 61 | Crypto::DEFAULT => sig.recover(&hash), 62 | _ => Err("Mismatched encryption algorithm".to_string()), 63 | } 64 | } 65 | } 66 | 67 | impl FromStr for UnverifiedTransaction { 68 | type Err = ToolError; 69 | 70 | /// Parse UnverifiedTransaction from hex string 71 | fn from_str(content: &str) -> Result { 72 | let bytes = hex::decode(remove_0x(content)).map_err(ToolError::Decode)?; 73 | parse_from_bytes(&bytes).map_err(ToolError::Proto) 74 | } 75 | } 76 | 77 | impl Transaction { 78 | /// Sign data 79 | pub fn sign(&self, sk: PrivateKey) -> SignedTransaction { 80 | let key_pair = KeyPair::from_privkey(sk); 81 | let pubkey = key_pair.pubkey(); 82 | 83 | let unverified_tx = self.build_unverified(sk); 84 | 85 | // Build SignedTransaction 86 | let mut signed_tx = SignedTransaction::new(); 87 | signed_tx.set_signer(pubkey.to_vec()); 88 | let bytes: Vec = (&unverified_tx).write_to_bytes().unwrap(); 89 | 90 | let hash = match sk { 91 | PrivateKey::Secp256k1(_) => bytes.crypt_hash(Encryption::Secp256k1), 92 | PrivateKey::Ed25519(_) => bytes.crypt_hash(Encryption::Ed25519), 93 | PrivateKey::Sm2(_) => bytes.crypt_hash(Encryption::Sm2), 94 | PrivateKey::Null => H256::default(), 95 | }; 96 | 97 | signed_tx.set_tx_hash(hash.to_vec()); 98 | signed_tx.set_transaction_with_sig(unverified_tx); 99 | signed_tx 100 | } 101 | 102 | /// Build unverified transaction 103 | pub fn build_unverified(&self, sk: PrivateKey) -> UnverifiedTransaction { 104 | let mut unverified_tx = UnverifiedTransaction::new(); 105 | let bytes: Vec = self.write_to_bytes().unwrap(); 106 | 107 | let hash = match sk { 108 | PrivateKey::Secp256k1(_) => bytes.crypt_hash(Encryption::Secp256k1), 109 | PrivateKey::Ed25519(_) => bytes.crypt_hash(Encryption::Ed25519), 110 | PrivateKey::Sm2(_) => bytes.crypt_hash(Encryption::Sm2), 111 | PrivateKey::Null => H256::default(), 112 | }; 113 | 114 | unverified_tx.set_transaction(self.clone()); 115 | let signature = sign(&sk, &hash); 116 | unverified_tx.set_signature(signature.to_vec()); 117 | unverified_tx.set_crypto(Crypto::DEFAULT); 118 | unverified_tx 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod test { 124 | use super::*; 125 | 126 | // Just to show how to parse Transaction from bytes 127 | #[test] 128 | fn test_parse_from_bytes() { 129 | let content = hex::decode("0a580a28666666666666666666666666666666666666666666666666666666666666666666666666666666661220383865613735396361306465343537353930333965323664623866616633346618c0843d2098f7242a02abce12410eb039fe08783d62f30e1bb5542312e519e7f6bb84ba1c3c08101af902463fda5f1c0e4d54d93bab2541d0a4aa5b85e71dfbaf5206131db6d491b4ffd256e78c00").unwrap(); 130 | let tx: UnverifiedTransaction = parse_from_bytes(&content).unwrap(); 131 | assert_eq!("abce", hex::encode(&tx.transaction.get_ref().data)); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /cita-tool/src/rpctypes.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, convert::Into, default::Default, fmt}; 2 | 3 | use serde_json::{self, json}; 4 | 5 | /// JsonRpc params 6 | #[derive(Serialize, Deserialize, Clone)] 7 | pub struct JsonRpcParams { 8 | #[serde(flatten)] 9 | extra: HashMap, 10 | } 11 | 12 | impl JsonRpcParams { 13 | /// Create a JsonRpc Params 14 | pub fn new() -> Self { 15 | Default::default() 16 | } 17 | 18 | /// Insert params 19 | pub fn insert>(mut self, key: T, value: ParamsValue) -> Self { 20 | self.extra.insert(key.into(), value); 21 | self 22 | } 23 | 24 | /// Remove params 25 | pub fn remove>(&mut self, key: T) -> Option { 26 | self.extra.remove(&key.into()) 27 | } 28 | 29 | /// Get params 30 | pub fn get>(&self, key: T) -> Option<&ParamsValue> { 31 | self.extra.get(&key.into()) 32 | } 33 | } 34 | 35 | impl Default for JsonRpcParams { 36 | fn default() -> Self { 37 | let mut extra = HashMap::new(); 38 | extra.insert( 39 | String::from("jsonrpc"), 40 | ParamsValue::String("2.0".to_string()), 41 | ); 42 | JsonRpcParams { extra } 43 | } 44 | } 45 | 46 | impl fmt::Debug for JsonRpcParams { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 48 | write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) 49 | } 50 | } 51 | 52 | impl fmt::Display for JsonRpcParams { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 54 | write!(f, "{}", json!(self)) 55 | } 56 | } 57 | 58 | /// The params value of jsonrpc params 59 | #[derive(Clone, Deserialize, Serialize)] 60 | #[serde(untagged)] 61 | pub enum ParamsValue { 62 | /// Single string parameter 63 | String(String), 64 | /// Singe int parameter 65 | Int(u64), 66 | /// Multiple parameters 67 | List(Vec), 68 | /// Map of values 69 | Map(HashMap), 70 | /// bool 71 | Bool(bool), 72 | /// Null parameters 73 | Null, 74 | } 75 | 76 | impl fmt::Debug for ParamsValue { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 78 | write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) 79 | } 80 | } 81 | 82 | impl fmt::Display for ParamsValue { 83 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 84 | write!(f, "{}", json!(self)) 85 | } 86 | } 87 | 88 | /// The value of response result or error 89 | #[derive(Clone, Deserialize, Serialize)] 90 | #[serde(untagged)] 91 | pub enum ResponseValue { 92 | /// Map result 93 | Map(HashMap), 94 | /// Singe result 95 | Singe(ParamsValue), 96 | } 97 | 98 | impl fmt::Debug for ResponseValue { 99 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 100 | write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) 101 | } 102 | } 103 | 104 | impl fmt::Display for ResponseValue { 105 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 106 | write!(f, "{}", json!(self)) 107 | } 108 | } 109 | 110 | /// Jsonrpc response 111 | #[derive(Clone, Serialize, Deserialize, Default)] 112 | pub struct JsonRpcResponse { 113 | jsonrpc: String, 114 | #[serde(skip_serializing_if = "Option::is_none")] 115 | result: Option, 116 | #[serde(skip_serializing_if = "Option::is_none")] 117 | error: Option, 118 | id: u64, 119 | } 120 | 121 | impl JsonRpcResponse { 122 | /// Get result 123 | pub fn result(&self) -> Option { 124 | self.result.clone() 125 | } 126 | 127 | /// Get error 128 | pub fn error(&self) -> Option { 129 | self.error.clone() 130 | } 131 | 132 | /// Determine if the query is normal 133 | pub fn is_ok(&self) -> bool { 134 | self.result.is_some() 135 | } 136 | } 137 | 138 | impl fmt::Debug for JsonRpcResponse { 139 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 140 | write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) 141 | } 142 | } 143 | 144 | impl fmt::Display for JsonRpcResponse { 145 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 146 | write!(f, "{}", json!(self)) 147 | } 148 | } 149 | 150 | /// Error 151 | #[derive(Clone, Serialize, Deserialize)] 152 | pub struct ErrorResponse { 153 | code: i64, 154 | message: String, 155 | /// Optional data 156 | #[serde(skip_serializing_if = "Option::is_none")] 157 | pub data: Option, 158 | } 159 | 160 | impl ErrorResponse { 161 | /// Get error message 162 | pub fn message(&self) -> String { 163 | self.message.clone() 164 | } 165 | 166 | /// Get error code 167 | pub fn code(&self) -> i64 { 168 | self.code 169 | } 170 | } 171 | 172 | impl fmt::Debug for ErrorResponse { 173 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 174 | write!(f, "{}", serde_json::to_string_pretty(self).unwrap()) 175 | } 176 | } 177 | 178 | impl fmt::Display for ErrorResponse { 179 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 180 | write!(f, "{}", json!(self)) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Build cita-cli docker image 2 | 3 | ## Build the arctifact 4 | 5 | - First, install `musl-gcc`, default on `/usr/local/musl` 6 | 7 | ```bash 8 | $ wget https://www.musl-libc.org/releases/musl-1.1.19.tar.gz 9 | $ tar -xzvf musl-1.1.19.tar.gz 10 | $ cd musl-1.1.19/ 11 | $ ./configure && make && sudo make install 12 | $ sudo ln -sf /usr/local/musl/bin/musl-gcc /usr/local/bin/musl-gcc 13 | ``` 14 | 15 | - Second, add `x86_64-unknown-linux-musl` toolchain 16 | 17 | ```bash 18 | $ rustup target add x86_64-unknown-linux-musl 19 | ``` 20 | 21 | - Third, build 22 | 23 | ```bash 24 | $ cargo install --target x86_64-unknown-linux-musl --path . 25 | ``` 26 | 27 | ## Build image 28 | 29 | - First, copy the arctifact 30 | 31 | ```shell 32 | cd release 33 | cp ~/.cargo/bin/cita-cli . 34 | ``` 35 | 36 | - Second, build images 37 | 38 | ```shell 39 | docker image build -t cita-ce-cli . 40 | ``` -------------------------------------------------------------------------------- /docker/release/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | LABEL maintainer="Rivtower Technologies " 3 | WORKDIR /opt/cita 4 | ADD cita-cli /bin 5 | CMD sleep 1 && cita-cli 6 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable -------------------------------------------------------------------------------- /tool-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tool-derive" 3 | version = "0.1.0" 4 | authors = ["piaoliu <441594700@qq.com>"] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "tool_derive" 9 | proc-macro = true 10 | 11 | [dependencies] 12 | proc-macro2 = "^1.0.0" 13 | quote = "^1.0.0" 14 | syn = "^1.0.0" 15 | -------------------------------------------------------------------------------- /tool-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "128"] 2 | 3 | extern crate proc_macro; 4 | 5 | use proc_macro::TokenStream; 6 | use quote::quote; 7 | use std::collections::HashSet; 8 | use syn::DeriveInput; 9 | 10 | #[proc_macro_derive(ContractExt, attributes(contract))] 11 | pub fn contract(input: TokenStream) -> TokenStream { 12 | // Parse the string representation 13 | let input: DeriveInput = syn::parse(input).unwrap(); 14 | let mut trait_name = "".to_string(); 15 | let mut address = "".to_string(); 16 | let mut path = "".to_string(); 17 | for meta_items in input.attrs.iter().filter_map(get_contract_meta_items) { 18 | for meta_item in meta_items { 19 | match meta_item { 20 | // parse #[contract(name = "foo")] 21 | syn::NestedMeta::Meta(syn::Meta::NameValue(ref m)) if m.path.is_ident("name") => { 22 | if let syn::Lit::Str(ref lit) = m.lit { 23 | trait_name = lit.value(); 24 | // println!("{}", lit.value()); 25 | } 26 | } 27 | // parse #[contract(addr = "foo")] 28 | syn::NestedMeta::Meta(syn::Meta::NameValue(ref m)) if m.path.is_ident("addr") => { 29 | if let syn::Lit::Str(ref lit) = m.lit { 30 | address = lit.value(); 31 | // println!("{}", lit.value()); 32 | } 33 | } 34 | // parse #[contract(path = "foo")] 35 | syn::NestedMeta::Meta(syn::Meta::NameValue(ref m)) if m.path.is_ident("path") => { 36 | if let syn::Lit::Str(ref lit) = m.lit { 37 | path = lit.value(); 38 | // println!("{}", lit.value()); 39 | } 40 | } 41 | _ => {} 42 | } 43 | } 44 | } 45 | if path == "" { 46 | panic!("path must set"); 47 | } 48 | if address == "" { 49 | panic!("contract address must set"); 50 | } 51 | if trait_name == "" { 52 | panic!("trait name must set"); 53 | } 54 | // struct name 55 | let name = input.ident; 56 | // parse str to LitStr 57 | let path = syn::LitStr::new(&path, proc_macro2::Span::call_site()); 58 | // parse str to Ident 59 | let trait_name = syn::Ident::new(&trait_name, proc_macro2::Span::call_site()); 60 | let address = syn::LitStr::new(&address, proc_macro2::Span::call_site()); 61 | 62 | let output = if let syn::Data::Struct(data) = input.data { 63 | let mut field = vec![ 64 | Some(syn::Ident::new("client", proc_macro2::Span::call_site())), 65 | Some(syn::Ident::new("address", proc_macro2::Span::call_site())), 66 | Some(syn::Ident::new("contract", proc_macro2::Span::call_site())), 67 | ] 68 | .into_iter() 69 | .collect::>>(); 70 | 71 | if let syn::Fields::Named(ref x) = data.fields { 72 | if x.named.len() < 3 { 73 | panic!("Must have 3 more field"); 74 | } 75 | } 76 | 77 | data.fields.iter().for_each(|i| { 78 | field.remove(&i.ident); 79 | }); 80 | if !field.is_empty() { 81 | panic!("Contract client must have client/address/contract"); 82 | } 83 | quote!( 84 | impl #name 85 | where T: ClientExt 86 | { 87 | /// Create a Contract Client 88 | pub fn new(client: T, address_str: &str, contract_json: &str) -> Self { 89 | let address = Address::from_str(remove_0x(address_str)).unwrap_or_else(|_| Address::default()); 90 | let contract = Contract::load(contract_json.as_bytes()).unwrap(); 91 | #name { 92 | client, 93 | address, 94 | contract, 95 | } 96 | } 97 | } 98 | impl ContractCall for #name 99 | where T: ClientExt 100 | { 101 | fn prepare_call_args( 102 | &self, 103 | name: &str, 104 | values: &[&str], 105 | to_addr: Option
, 106 | ) -> Result<(String, String), ToolError> { 107 | let values = values.iter().map(ToString::to_string).collect::>(); 108 | let code = contract_encode_input(&self.contract, name, values.as_slice(), false)?; 109 | let code = format!("0x{}", code); 110 | let to_address = to_addr.unwrap_or(self.address); 111 | let to_address = format!("{:?}", to_address); 112 | Ok((code, to_address)) 113 | } 114 | 115 | fn contract_send_tx( 116 | &mut self, 117 | name: &str, 118 | values: &[&str], 119 | quota: Option, 120 | to_addr: Option
, 121 | ) -> Result { 122 | let (code, to_address) = self.prepare_call_args(name, values, to_addr)?; 123 | let tx_options = TransactionOptions::new() 124 | .set_code(code.as_str()) 125 | .set_address(to_address.as_str()) 126 | .set_quota(quota); 127 | self.client.send_raw_transaction( 128 | tx_options, 129 | ) 130 | } 131 | 132 | fn contract_call( 133 | &self, 134 | name: &str, 135 | values: &[&str], 136 | to_addr: Option
, 137 | height: Option<&str>, 138 | ) -> Result { 139 | let (code, to_address) = self.prepare_call_args(name, values, to_addr)?; 140 | self.client.call( 141 | None, 142 | to_address.as_str(), 143 | Some(code.as_str()), 144 | height.unwrap_or_else(|| "latest"), 145 | ) 146 | } 147 | } 148 | impl #trait_name for #name 149 | where T: ClientExt, 150 | { 151 | fn create(client: T) -> Self { 152 | static ABI: &str = include_str!(#path); 153 | static ADDRESS: &str = #address; 154 | Self::new(client, ADDRESS, ABI) 155 | } 156 | } 157 | ) 158 | } else { 159 | panic!("Only impl to struct") 160 | }; 161 | 162 | // Return the generated impl 163 | output.into() 164 | } 165 | 166 | /// Filter contract attribute like #[contract(foo = bar)] 167 | fn get_contract_meta_items(attr: &syn::Attribute) -> Option> { 168 | if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "contract" { 169 | match attr.parse_meta() { 170 | Ok(syn::Meta::List(ref meta)) => Some(meta.nested.iter().cloned().collect()), 171 | _ => { 172 | // TODO: produce an error 173 | None 174 | } 175 | } 176 | } else { 177 | None 178 | } 179 | } 180 | --------------------------------------------------------------------------------