eosc
is a cross-platform (Windows, Mac and Linux) command-line tool
for interacting with an EOS.IO blockchain.
It contains tools for voting and a Vault to securely store private keys.
It is based on the eos-go
library, used in eos-bios
.
This first release holds simple tools, but a whole cleos
-like
swiss-army-knife is being developed and will be released shortly after
mainnet launch. Source code for most operations is already available
in this repository.
- Install from https://github.com/eoscanada/eosc/releases
or
- Build from source with:
go get -u -v github.com/eoscanada/eosc/eosc
or
- If you are on MacOS and use Homebrew:
brew install eoscanada/tap/eosc
Once installed run:
eosc vault create --import
to import your keys and follow instructions.
Then set your environment variable to the API URL of your choice, optionally setting some HTTP headers:
export EOSC_GLOBAL_API_URL=https://your-favorite-endpoint
export EOSC_GLOBAL_HTTP_HEADER_0="Authorization: bearer abcdef12323453452565676589"
export EOSC_GLOBAL_HTTP_HEADER_1="Origin: https://something...
Then you can run commands on the chain, ex:
eosc get info
eosc transfer fromaccnt toaccnt 0.0001 --memo "Sent with eosc"
-
All global flags (those you get from eosc –help) can be set with the following pattern: EOSC_GLOBAL_FLAG_NAME. The most useful are:
EOSC_GLOBAL_WALLET_URL
->--wallet-url
EOSC_GLOBAL_VAULT_FILE
->--vault-file
-
All (sub)command flags map to the following pattern: EOSC_COMMAND_SUBCOMMAND_CMD_FLAG_NAME (ex:
EOSC_FORUM_POST_CMD_REPLY_TO
->eosc forum post --reply-to=...
-
EOSC_GLOBAL_INSECURE_VAULT_PASSPHRASE
allows you to input the passphrase directly in an environment variable (useful for test automation, risky for most other uses) -
EOSC_GLOBAL_HTTP_HEADER_0
(available for indexes 0 to 25)
The eosc vault
is a simple yet powerful EOS wallet.
You can import externally generated private keys with vault create --import
call.
You will then be asked to paste your private key in an interactive prompt. The private key is not shown on screen during the import. It will display the corresponding public key which you should cross-check.
$ eosc vault create --import -c "Imported key"
Enter passphrase to encrypt your vault: ****
Confirm passphrase: ****
Type your first private key: ****
Type your next private key or hit ENTER if you are done:
Imported 1 keys. Let's secure them before showing the public keys.
Wallet file "./eosc-vault.json" created. Here are your public keys:
- EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
Your private keys will be encrypted using your passphrase. See below for cryptographic primitives used.
Create a new wallet with:
$ eosc vault create --keys 2
Created 2 keys. Let's secure them before showing the public keys.
Enter passphrase to encrypt your keys:
Confirm passphrase:
Wallet file "./eosc-vault.json" created. Here are your public keys:
- EOS7MEGq9FVb2Ve4bsZEan1t146TKCyo8dKtLvihrNhGbPLCPLjXd
- EOS5QoyZwJvpPjmZAa3HdgTn2FdNABBffXLD95WPagiARmaAHMhin
To add an externally generated private key to an existing vault, use:
$ eosc vault add
Vault file not found, creating a new wallet
Type your first private key: [insert your private key here, NOT shown]
Type your next private key or hit ENTER if you are done:
Keys imported. Let's secure them before showing the public keys.
Enter passphrase to encrypt your keys:
Confirm passphrase:
Wallet file "./eosc-vault.json" written. Here are your ADDED public keys:
- EOS5tb61aZMAfQqKDsnkscFq76JXxNdi7ZhkUmkVZUkU4zPzfeAFx
Total keys writteN: 3
The vault operations do zero network calls and can be done offline. The file is encrypted, can safely be archived and is future proof.
eosc
has everything needed for managing an airtight offline wallet.
Here is a sample use. From within /tmp/airtight
, run:
eosc vault create --import --comment "Airtight wallet"
which will output:
PLEASE READ:
We are now going to ask you to paste your private keys, one at a time.
They will not be shown on screen.
Please verify that the public keys printed on screen correspond to what you have noted
Paste your first private key: <em>[PASTE PRIVATE KEY HERE]</em>
- Scanned private key corresponding to EOS5J53bNtH5H1bJTyP237fr5LF6eQSQohfGY5iMCgpC4HpXApJBr
Paste your next private key or hit ENTER if you are done: <em>[PASTE SECOND PRIVATE KEY HERE]</em>
- Scanned private key corresponding to EOS6tgsdv6S7N1GYWgX8QEBAsanAwXwuaEkv11GGtteyk5ELqSzVP
Paste your next private key or hit ENTER if you are done: <em>[HIT ENTER HERE]</em>
Imported 2 keys.
You will be asked to provide a passphrase to secure your newly created vault.
Make sure you make it long and strong.
Enter passphrase to encrypt your vault: <em>[ENTER YOUR PASSPHRASE HERE]</em>
Confirm passphrase: <em>[RE-ENTER YOUR PASSPHRASE HERE]</em>
Wallet file "./eosc-vault.json" written to disk.
Here are the keys that were ADDED during this operation (use `list` to see them all):
- EOS5J53bNtH5H1bJTyP237fr5LF6eQSQohfGY5iMCgpC4HpXApJBr
- EOS6tgsdv6S7N1GYWgX8QEBAsanAwXwuaEkv11GGtteyk5ELqSzVP
Total keys stored: 2
This operation creates your eosc-vault.json
wallet. It can be done
completely offline as it does not touch the network at all.
Now, let's try using it. Issue those commands from a computer with internet access (which in turn does not have access to your eosc vault wallet):
eosc transfer airtight11111 cancancan123 1.0000 -m "Can't say I haven't paid you now" --write-transaction transaction.json --skip-sign --expiration 3600
This will output:
{
"expiration": "2018-08-27T04:18:43",
"ref_block_num": 43579,
"ref_block_prefix": 3787183056,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio.token",
"name": "transfer",
"authorization": [
{
"actor": "airtight1111",
"permission": "active"
}
],
"data": {
"from": "airtight1111",
"to": "cancancan123",
"quantity": "0.0001 EOS",
"memo": "Can't say I haven't paid you now"
}
}
],
"transaction_extensions": [],
"signatures": [],
"context_free_data": []
}
---
Transaction written to "../transaction.json"
Sign offline with: --offline-chain-id=5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191
Above is a pretty-printed representation of the outputted file
Now copy the transaction.json
file to another computer. Note the --offline-chain-id=...
output above.
On the airtight computer, run:
eosc tx sign path/to/transaction.json --offline-sign-key EOS6tgsdv6S7N1GYWgX8QEBAsanAwXwuaEkv11GGtteyk5ELqSzVP --offline-chain-id=5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191 --write-transaction signedtx.json
You can enter as many --offline-sign-key
as you want, or separate each entry with a ,
(comma), like this: --offline-sign-key EOS6tgsdv6S7N1GYWgX8QEBAsanAwXwuaEkv11GGtteyk5ELqSzVP,EOS5J53bNtH5H1bJTyP237fr5LF6eQSQohfGY5iMCgpC4HpXApJBr
. This will output:
Enter passphrase to decrypt your vault:
{
"expiration": "2018-08-27T04:18:43",
"ref_block_num": 43579,
"ref_block_prefix": 3787183056,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio.token",
"name": "transfer",
"authorization": [
{
"actor": "airtight1111",
"permission": "active"
}
],
"data": "104208b93197af33304498064d83a641010000000000000004454f53000000002043616e277420736179204920686176656e2774207061696420796f75206e6f77"
}
],
"transaction_extensions": [],
"signatures": [
"SIG_K1_KbsvdjfXTzbn7DaghCbFyaxXnv9BHJaAVWZkeJ3AbvnznNgipSLj7BbZajZ1ECZEWKSMFtMbuqfUWQGq2tDzW2n7Fz5KTV",
"SIG_K1_K24N3kUfTLMJGwUUWV2qNiyhxAJsbVtpXh2KUj3SHGLTivPBru46QYX9v9gQj7G2yKHp6eZ786hHfJwuzjeZFF7atPfpTY"
],
"context_free_data": []
}
---
Transaction written to "signedtx.json"
Sign offline with: --offline-chain-id=5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191
Above is a pretty-printed representation of the outputted file
You are now ready to bring signedtx.json
back to the online computer, and run:
eosc tx push signedtx.json
This will succeed, in which case you're done, or fail with some of these:
-
transaction bears irrelevant signatures from these keys: [\"EOS5J53bNtH5H1bJTyP237fr5LF6eQSQohfGY5iMCgpC4HpXApJBr\"]
: this means you didn't need to sign with this key to authorize theactor@permission
you specified in the transaction's actions. -
UnAuthorized
errors: this means you have not signed the transaction with the keys required to authorize theactor@permission
specified in the transaction's actions. -
Transaction's reference block did not match.
: this means you didn't create the transaction from the same chain you're trying to push it to. -
expired transaction
: you need to do the whole loop within the timeframe of the original--expiration
. If you give--expiration
more than an hour, note that you can only submit the transaction to the chain in the last hour of expiration. -
some other assertion errors, which are normal errors that would have occurred if you tried to sign it online, investigate with the contract in question.
For added security with offline transaction signing, consider using
the multisig
facility of the EOS blockchain.
It is possible to propose a transaction on-chain (through eosc multisig propose
), reviewable by all, and prepare an offline
transaction simply to approve that transaction. Using this method,
you could have X number of airtight computers that each provide one
piece of the puzzle, controlled by different individuals, without the
need to stitch transaction files, or gather signatures into a single
place.
The cryptography used is NaCl (C implementation, Javascript port, Go version, which we use). And key derivation is Argon2, using the Go library here.
You can inspect the crypto code in our codebase regarding the
passphrase
implementation: it is 61 lines,
including blanks and comments.
Q: Why not use cleos
instead ?
A: cleos
is hard to compile, being in C++, as it requires a huge
toolchain. eosc
works on Windows where cleos
doesn't. eosc
contains a wallet inside, and is able to use it to sign some
transactions, cleos
interfaces with yet another program (keosd
) in
order to sign transactions, making it more complex to use. eosc
brings keosd
and cleos
together in a swiss-army-knify package.