UNPKG

@stacks/cli

Version:
1,441 lines (1,408 loc) • 121 kB
import Ajv from 'ajv'; import * as process from 'process'; import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs'; export const NAME_PATTERN = '^([0-9a-z_.+-]{3,37})$'; export const NAMESPACE_PATTERN = '^([0-9a-z_-]{1,19})$'; export const ADDRESS_CHARS = '[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{1,35}'; export const C32_ADDRESS_CHARS = '[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+'; export const ADDRESS_PATTERN = `^(${ADDRESS_CHARS})$`; export const ID_ADDRESS_PATTERN = `^ID-${ADDRESS_CHARS}$`; export const STACKS_ADDRESS_PATTERN = `^(${C32_ADDRESS_CHARS})$`; // hex private key export const PRIVATE_KEY_PATTERN = '^([0-9a-f]{64,66})$'; // hex private key, no compression export const PRIVATE_KEY_UNCOMPRESSED_PATTERN = '^([0-9a-f]{64})$'; // nosign:addr export const PRIVATE_KEY_NOSIGN_PATTERN = `^nosign:${ADDRESS_CHARS}$`; // m,pk1,pk2,...,pkn export const PRIVATE_KEY_MULTISIG_PATTERN = '^([0-9]+),([0-9a-f]{64,66},)*([0-9a-f]{64,66})$'; // segwit:p2sh:m,pk1,pk2,...,pkn export const PRIVATE_KEY_SEGWIT_P2SH_PATTERN = '^segwit:p2sh:([0-9]+),([0-9a-f]{64,66},)*([0-9a-f]{64,66})$'; // any private key pattern we support export const PRIVATE_KEY_PATTERN_ANY = `${PRIVATE_KEY_PATTERN}|${PRIVATE_KEY_MULTISIG_PATTERN}|${PRIVATE_KEY_SEGWIT_P2SH_PATTERN}|${PRIVATE_KEY_NOSIGN_PATTERN}`; export const PUBLIC_KEY_PATTERN = '^([0-9a-f]{66,130})$'; export const INT_PATTERN = '^-?[0-9]+$'; export const ZONEFILE_HASH_PATTERN = '^([0-9a-f]{40})$'; export const URL_PATTERN = '^http[s]?://.+$'; export const SUBDOMAIN_PATTERN = '^([0-9a-z_+-]{1,37}).([0-9a-z_.+-]{3,37})$'; export const TXID_PATTERN = '^([0-9a-f]{64})$'; export const BOOLEAN_PATTERN = '^(0|1|true|false)$'; export interface CLI_LOG_CONFIG_TYPE { level: string; handleExceptions: boolean; timestamp: boolean; stringify: boolean; colorize: boolean; json: boolean; } export interface CLI_CONFIG_TYPE { blockstackAPIUrl: string; blockstackNodeUrl: string; broadcastServiceUrl: string; utxoServiceUrl: string; logConfig: CLI_LOG_CONFIG_TYPE; bitcoindUsername?: string; bitcoindPassword?: string; } const LOG_CONFIG_DEFAULTS: CLI_LOG_CONFIG_TYPE = { level: 'info', handleExceptions: true, timestamp: true, stringify: true, colorize: true, json: true, }; const CONFIG_DEFAULTS: CLI_CONFIG_TYPE = { blockstackAPIUrl: 'https://api.hiro.so', blockstackNodeUrl: 'https://api.hiro.so', broadcastServiceUrl: 'https://api.hiro.so/v2/transactions', utxoServiceUrl: 'https://blockchain.info', logConfig: LOG_CONFIG_DEFAULTS, }; const CONFIG_LOCALNET_DEFAULTS = { blockstackAPIUrl: `http://localhost:20443`, blockstackNodeUrl: `http://localhost:20443`, broadcastServiceUrl: `http://localhost:20443/v2/transactions`, utxoServiceUrl: `http://localhost:18332`, logConfig: Object.assign({}, LOG_CONFIG_DEFAULTS, { level: 'debug' }), }; const CONFIG_TESTNET_DEFAULTS = { blockstackAPIUrl: `https://api.testnet.hiro.so`, blockstackNodeUrl: `https://api.testnet.hiro.so`, broadcastServiceUrl: `https://api.testnet.hiro.so/v2/transactions`, utxoServiceUrl: `https://blockchain.info`, // todo: this likely doesn't work anymore logConfig: Object.assign({}, LOG_CONFIG_DEFAULTS, { level: 'debug' }), }; export const DEFAULT_CONFIG_PATH = '~/.blockstack-cli.conf'; export const DEFAULT_CONFIG_TESTNET_PATH = path.join(os.homedir(), '.blockstack-cli-testnet.conf'); export const DEFAULT_MAX_ID_SEARCH_INDEX = 256; interface CLI_PROP_ITEM { name: string; type: 'string'; realtype: string; pattern?: string; } interface CLI_PROP { [index: string]: { type: 'array'; items: CLI_PROP_ITEM[]; minItems: number; maxItems: number; help: string; group: string; }; } // CLI usage export const CLI_ARGS = { type: 'object', properties: { announce: { type: 'array', items: [ { name: 'message_hash', type: 'string', realtype: 'zonefile_hash', pattern: ZONEFILE_HASH_PATTERN, }, { name: 'owner_key', type: 'string', realtype: 'private_key', pattern: `${PRIVATE_KEY_PATTERN_ANY}`, }, ], minItems: 2, maxItems: 2, help: 'Broadcast a message on the blockchain for subscribers to read. ' + 'The `MESSAGE_HASH` argument must be the hash of a previously-announced zone file. ' + 'The `OWNER_KEY` used to sign the transaction must correspond to the Blockstack ID ' + 'to which other users have already subscribed. `OWNER_KEY` can be a single private key ' + 'or a serialized multisig private key bundle.\n' + '\n' + 'If this command succeeds, it will print a transaction ID. The rest of the Blockstack peer ' + 'network will process it once the transaction reaches 7 confirmations.\n' + '\n' + 'Examples:\n' + '```console\n' + ' $ # Tip: You can obtain the owner key with the get_owner_keys command\n' + ' $ export OWNER_KEY="136ff26efa5db6f06b28f9c8c7a0216a1a52598045162abfe435d13036154a1b01"\n' + ' $ stx announce 737c631c7c5d911c6617993c21fba731363f1cfe "$OWNER_KEY"\n' + ' d51749aeec2803e91a2f8bdec8d3e413491fd816b4962372b214ab74acb0bba8\n' + '\n' + ' $ export OWNER_KEY="2,136ff26efa5db6f06b28f9c8c7a0216a1a52598045162abfe435d13036154a1b01,1885cba486a42960499d1f137ef3a475725ceb11f45d74631f9928280196f67401,2418981c7f3a91d4467a65a518e14fafa30e07e6879c11fab7106ea72b49a7cb01"\n' + ' $ stx announce 737c631c7c5d911c6617993c21fba731363f1cfe "$OWNER_KEY"\n' + ' 8136a1114098893b28a693e8d84451abf99ee37ef8766f4bc59808eed76968c9\n' + '```\n' + '\n', group: 'Peer Services', }, authenticator: { type: 'array', items: [ { name: 'app_gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', pattern: '.+', }, { name: 'profile_gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'port', type: 'string', realtype: 'portnum', pattern: '^[0-9]+', }, ], minItems: 2, maxItems: 4, help: 'Run an authentication endpoint for the set of names owned ' + 'by the given backup phrase. Send applications the given Gaia hub URL on sign-in, ' + 'so the application will use it to read/write user data.\n' + '\n' + 'You can supply your encrypted backup phrase instead of the raw backup phrase. If so, ' + 'then you will be prompted for your password before any authentication takes place.\n' + '\n' + 'Example:\n' + '```console\n' + ' $ export BACKUP_PHRASE="oak indicate inside poet please share dinner monitor glow hire source perfect"\n' + ' $ export APP_GAIA_HUB="https://1.2.3.4"\n' + ' $ export PROFILE_GAIA_HUB="https://hub.blockstack.org"\n' + ' $ stx authenticator "$APP_GAIA_HUB" "$BACKUP_PHRASE" "$PROFILE_GAIA_HUB" 8888\n' + ' Press Ctrl+C to exit\n' + ' Authentication server started on 8888\n' + '```\n', group: 'Authentication', }, balance: { type: 'array', items: [ { name: 'address', type: 'string', realtype: 'address', pattern: `${ADDRESS_PATTERN}|${STACKS_ADDRESS_PATTERN}`, }, ], minItems: 1, maxItems: 1, help: 'Query the balance of an account. Returns the balances of each kind of token ' + 'that the account owns. The balances will be in the *smallest possible units* of the ' + 'token (i.e. satoshis for BTC, microStacks for Stacks, etc.).\n' + '\n' + 'Example:\n' + '```console\n' + ' $ stx balance 16pm276FpJYpm7Dv3GEaRqTVvGPTdceoY4\n' + ' {\n' + ' "BTC": "123456"\n' + ' "STACKS": "123456"\n' + ' }\n' + ' $ stx balance SPZY1V53Z4TVRHHW9Z7SFG8CZNRAG7BD8WJ6SXD0\n' + ' {\n' + ' "BTC": "123456"\n' + ' "STACKS": "123456"\n' + ' }' + '```\n', group: 'Account Management', }, can_stack: { type: 'array', items: [ { name: 'amount', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'cycles', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'pox_address', type: 'string', realtype: 'address', pattern: `${ADDRESS_PATTERN}`, }, { name: 'stx_address', type: 'string', realtype: 'address', pattern: `${STACKS_ADDRESS_PATTERN}`, }, ], minItems: 4, maxItems: 4, help: 'Check if specified account can stack a number of Stacks tokens for given number of cycles.\n' + '\n' + 'Example:\n' + '```console\n' + ' $ stx can_stack 10000000 20 16pm276FpJYpm7Dv3GEaRqTVvGPTdceoY4 SPZY1V53Z4TVRHHW9Z7SFG8CZNRAG7BD8WJ6SXD0\n' + ' {\n' + ' "eligible": true\n' + ' }\n' + '```\n', group: 'Account Management', }, call_contract_func: { type: 'array', items: [ { name: 'contract_address', type: 'string', realtype: 'address', pattern: `${STACKS_ADDRESS_PATTERN}`, }, { name: 'contract_name', type: 'string', realtype: 'string', pattern: '^[a-zA-Z]([a-zA-Z0-9]|[-_])*$', }, { name: 'function_name', type: 'string', realtype: 'string', pattern: '^[a-zA-Z]([a-zA-Z0-9]|[-_!?])*$', }, { name: 'fee', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'nonce', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'payment_key', type: 'string', realtype: 'private_key', pattern: `${PRIVATE_KEY_PATTERN_ANY}`, }, { name: 'function_args', type: 'string', realtype: 'string', pattern: '.+', }, ], minItems: 6, maxItems: 7, help: 'Call a function in a deployed Clarity smart contract.\n' + '\n' + 'If the command succeeds, it prints out a transaction ID.' + '\n' + 'Example:\n' + '```console\n' + ' $ export PAYMENT="bfeffdf57f29b0cc1fab9ea197bb1413da2561fe4b83e962c7f02fbbe2b1cd5401"\n' + ' $ stx call_contract_func SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X contract_name' + ' contract_function 1 0 "$PAYMENT"\n' + ' {\n' + " txid: '0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'," + " transaction: 'https://explorer.hiro.so/txid/0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'" + ' }\n' + '```\n' + '\n' + 'You can also provide function arguments directly instead of being prompted for them:\n' + '```console\n' + ' $ stx call_contract_func SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X contract_name' + ' contract_function 1 0 "$PAYMENT" \'u100, true, "some-string", { a: u"str-utf8", b: 2 }\'\n' + '```\n' + '\n', group: 'Account Management', }, call_read_only_contract_func: { type: 'array', items: [ { name: 'contract_address', type: 'string', realtype: 'address', pattern: `${STACKS_ADDRESS_PATTERN}`, }, { name: 'contract_name', type: 'string', realtype: 'string', pattern: '^[a-zA-Z]([a-zA-Z0-9]|[-_])*$', }, { name: 'function_name', type: 'string', realtype: 'string', pattern: '^[a-zA-Z]([a-zA-Z0-9]|[-_!?])*$', }, { name: 'sender_address', type: 'string', realtype: 'address', pattern: `${STACKS_ADDRESS_PATTERN}`, }, ], minItems: 4, maxItems: 4, help: 'Call a read-only function in a deployed Clarity smart contract.\n' + '\n' + 'If the command succeeds, it prints out a Clarity value.' + '\n' + 'Example:\n' + '```console\n' + ' $ stx call_read_only_contract_func SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X contract_name' + ' contract_function SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X\n' + ' {\n' + " txid: '0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'," + " transaction: 'https://explorer.hiro.so/txid/0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'" + ' }\n' + '```\n' + '\n', group: 'Account Management', }, decode_cv: { type: 'array', items: [ { name: 'clarity_value', type: 'string', realtype: 'string', pattern: '-|^(0x|0X)?[a-fA-F0-9]+$', }, { name: 'format', type: 'string', realtype: 'format', pattern: '^(repr|pretty|json)$', }, ], minItems: 1, maxItems: 4, help: 'Decode a serialized Clarity value.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ stx decode_cv 0x050011deadbeef11ababffff11deadbeef11ababffff\n' + ' S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S\n' + ' $ stx decode_cv --format json SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW\n' + ' {"type":"principal","value":"S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S"}\n' + ' $ echo 0x050011deadbeef11ababffff11deadbeef11ababffff | stx decode_cv -\n' + ' S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S\n' + '```\n', group: 'Utilities', }, convert_address: { type: 'array', items: [ { name: 'address', type: 'string', realtype: 'address', pattern: `${ADDRESS_PATTERN}|${STACKS_ADDRESS_PATTERN}`, }, ], minItems: 1, maxItems: 1, help: 'Convert a Bitcoin address to a Stacks address and vice versa.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ stx convert_address 12qdRgXxgNBNPnDeEChy3fYTbSHQ8nfZfD\n' + ' {\n' + ' "mainnet": {\n' + ' "STACKS": "SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW",\n' + ' "BTC": "12qdRgXxgNBNPnDeEChy3fYTbSHQ8nfZfD"\n' + ' }\n' + ' }\n' + ' $ stx convert_address SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW\n' + ' {\n' + ' "mainnet": {\n' + ' "STACKS": "SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW",\n' + ' "BTC": "12qdRgXxgNBNPnDeEChy3fYTbSHQ8nfZfD"\n' + ' }\n' + ' }\n' + ' $ stx convert_address SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW -t\n' + ' {\n' + ' "mainnet": {\n' + ' "STACKS": "SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW",\n' + ' "BTC": "12qdRgXxgNBNPnDeEChy3fYTbSHQ8nfZfD"\n' + ' },\n' + ' "testnet": {\n' + ' "STACKS": "STA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7DX96QAM",\n' + ' "BTC": "mhMaijcwVPcdAthFwmgLsaknTRt72GqQYo"\n' + ' }\n' + ' }\n' + ' $ stx convert_address STA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7DX96QAM\n' + ' {\n' + ' "mainnet": {\n' + ' "STACKS": "SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW",\n' + ' "BTC": "12qdRgXxgNBNPnDeEChy3fYTbSHQ8nfZfD"\n' + ' },\n' + ' "testnet": {\n' + ' "STACKS": "STA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7DX96QAM",\n' + ' "BTC": "mhMaijcwVPcdAthFwmgLsaknTRt72GqQYo"\n' + ' }\n' + ' }\n' + '```\n', group: 'Account Management', }, decrypt_keychain: { type: 'array', items: [ { name: 'encrypted_backup_phrase', type: 'string', realtype: 'encrypted_backup_phrase', pattern: '^[^ ]+$', }, { name: 'password', type: 'string', realtype: 'password', pattern: '.+', }, ], minItems: 1, maxItems: 2, help: 'Decrypt an encrypted backup phrase with a password. Decrypts to a 12-word ' + 'backup phrase if done correctly. The password will be prompted if not given.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # password is "asdf"\n' + ' $ stx decrypt_keychain "bfMDtOucUGcJXjZo6vkrZWgEzue9fzPsZ7A6Pl4LQuxLI1xsVF0VPgBkMsnSLCmYS5YHh7R3mNtMmX45Bq9sNGPfPsseQMR0fD9XaHi+tBg=\n' + ' Enter password:\n' + ' section amount spend resemble spray verify night immune tattoo best emotion parrot\n' + '```\n', group: 'Key Management', }, deploy_contract: { type: 'array', items: [ { name: 'source_file', type: 'string', realtype: 'path', pattern: '.+', }, { name: 'contract_name', type: 'string', realtype: 'string', pattern: '^[a-zA-Z]([a-zA-Z0-9]|[-_])*$', }, { name: 'fee', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'nonce', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'payment_key', type: 'string', realtype: 'private_key', pattern: `${PRIVATE_KEY_PATTERN_ANY}`, }, ], minItems: 5, maxItems: 5, help: 'Deploys a Clarity smart contract on the network.\n' + '\n' + 'If the command succeeds, it prints out a transaction ID.' + '\n' + 'Example:\n' + '```console\n' + ' $ export PAYMENT="bfeffdf57f29b0cc1fab9ea197bb1413da2561fe4b83e962c7f02fbbe2b1cd5401"\n' + ' $ stx deploy_contract ./my_contract.clar my_contract 1 0 "$PAYMENT"\n' + ' {\n' + " txid: '0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'," + " transaction: 'https://explorer.hiro.so/txid/0x2e33ad647a9cedacb718ce247967dc705bc0c878db899fdba5eae2437c6fa1e1'" + ' }\n' + '```\n' + '\n', group: 'Account Management', }, docs: { type: 'array', items: [ { name: 'format', type: 'string', realtype: 'output_format', pattern: '^json$', }, ], minItems: 0, maxItems: 1, help: 'Dump the documentation for all commands as JSON to standard out.', group: 'CLI', }, encrypt_keychain: { type: 'array', items: [ { name: 'backup_phrase', type: 'string', realtype: 'backup_phrase', pattern: '.+', }, { name: 'password', type: 'string', realtype: 'password', pattern: '.+', }, ], minItems: 1, maxItems: 2, help: 'Encrypt a 12-word backup phrase, which can be decrypted later with the ' + '`decrypt_backup_phrase` command. The password will be prompted if not given.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # password is "asdf"\n' + ' $ stx encrypt_keychain "section amount spend resemble spray verify night immune tattoo best emotion parrot"\n' + ' Enter password:\n' + ' Enter password again:\n' + ' M+DnBHYb1fgw4N3oZ+5uTEAua5bAWkgTW/SjmmBhGGbJtjOtqVV+RrLJEJOgT35hBon4WKdGWye2vTdgqDo7+HIobwJwkQtN2YF9g3zPsKk=' + '```\n', group: 'Key Management', }, faucet: { type: 'array', items: [ { name: 'address', type: 'string', realtype: 'address', pattern: `${ADDRESS_PATTERN}|${STACKS_ADDRESS_PATTERN}`, }, ], minItems: 1, maxItems: 1, help: 'Encrypt a 12-word backup phrase, which can be decrypted later with the ' + '`decrypt_backup_phrase` command. The password will be prompted if not given.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # password is "asdf"\n' + ' $ blockstack-cli encrypt_keychain "section amount spend resemble spray verify night immune tattoo best emotion parrot"\n' + ' Enter password:\n' + ' Enter password again:\n' + ' M+DnBHYb1fgw4N3oZ+5uTEAua5bAWkgTW/SjmmBhGGbJtjOtqVV+RrLJEJOgT35hBon4WKdGWye2vTdgqDo7+HIobwJwkQtN2YF9g3zPsKk=' + '```\n', group: 'Key Management', }, gaia_dump_bucket: { type: 'array', items: [ { name: 'name_or_id_address', type: 'string', realtype: 'name_or_id_address', pattern: `${ID_ADDRESS_PATTERN}|${NAME_PATTERN}|${SUBDOMAIN_PATTERN}`, }, { name: 'app_origin', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, { name: 'dump_dir', type: 'string', realtype: 'path', pattern: '.+', }, ], minItems: 5, maxItems: 5, help: 'Download the contents of a Gaia hub bucket to a given directory. The `GAIA_HUB` argument ' + 'must correspond to the *write* endpoint of the Gaia hub -- that is, you should be able to fetch ' + '`$GAIA_HUB/hub_info`. If `DUMP_DIR` does not exist, it will be created.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ export BACKUP_PHRASE="section amount spend resemble spray verify night immune tattoo best emotion parrot\n' + ' $ stx gaia_dump_bucket hello.id.blockstack https://sample.app https://hub.blockstack.org "$BACKUP_PHRASE" ./backups\n' + ' Download 3 files...\n' + ' Download hello_world to ./backups/hello_world\n' + ' Download dir/format to ./backups/dir\\x2fformat\n' + ' Download /.dotfile to ./backups/\\x2f.dotfile\n' + ' 3\n' + '```\n', group: 'Gaia', }, gaia_getfile: { type: 'array', items: [ { name: 'blockstack_id', type: 'string', realtype: 'blockstack_id', pattern: `${NAME_PATTERN}|${SUBDOMAIN_PATTERN}$`, }, { name: 'app_origin', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'filename', type: 'string', realtype: 'filename', pattern: '.+', }, { name: 'app_private_key', type: 'string', realtype: 'private_key', pattern: PRIVATE_KEY_UNCOMPRESSED_PATTERN, }, { name: 'decrypt', type: 'string', realtype: 'boolean', pattern: BOOLEAN_PATTERN, }, { name: 'verify', type: 'string', realtype: 'boolean', pattern: BOOLEAN_PATTERN, }, ], minItems: 3, maxItems: 6, help: "Get a file from another user's Gaia hub. Prints the file data to stdout. If you " + 'want to read an encrypted file, and/or verify a signed file, then you must pass an app ' + 'private key, and pass 1 for `DECRYPT` and/or `VERIFY`. If the file is encrypted, and you do not ' + 'pass an app private key, then this command downloads the ciphertext. If the file is signed, ' + 'and you want to download its data and its signature, then you must run this command twice -- ' + 'once to get the file contents at `FILENAME`, and once to get the signature (whose name will be `FILENAME`.sig).\n' + '\n' + 'Gaia is a key-value store, so it does not have any built-in notion of directories. However, ' + 'most underlying storage systems do -- directory separators in the name of a file in ' + "Gaia may be internally treated as first-class directories (it depends on the Gaia hub's driver)." + 'As such, repeated directory separators will be treated as a single directory separator by this command. ' + 'For example, the file name `a/b.txt`, `/a/b.txt`, and `///a////b.txt` will be treated as identical.\n' + '\n' + 'Example without encryption:\n' + '\n' + '```console\n' + ' $ # Get an unencrypted, unsigned file\n' + ' $ stx gaia_getfile ryan.id http://public.ykliao.com statuses.json\n' + ' [{"id":0,"text":"Hello, Blockstack!","created_at":1515786983492}]\n' + '\n' + 'Example with encryption:\n' + '\n' + ' $ # Get an encrypted file without decrypting\n' + ' $ stx gaia_getfile ryan.id https://app.graphitedocs.com documentscollection.json\n' + ' ' + ' $ # Get an encrypted file, and decrypt it\n' + ' $ # Tip: You can obtain the app key with the get_app_keys command\n' + ' $ export APP_KEY="3ac770e8c3d88b1003bf4a0a148ceb920a6172bdade8e0325a1ed1480ab4fb19"\n' + ' $ stx gaia_getfile ryan.id https://app.graphitedocs.com documentscollection.json "$APP_KEY" 1 0\n' + '```\n', group: 'Gaia', }, gaia_putfile: { type: 'array', items: [ { name: 'gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'app_private_key', type: 'string', realtype: 'private_key', pattern: PRIVATE_KEY_UNCOMPRESSED_PATTERN, }, { name: 'data_path', type: 'string', realtype: 'path', pattern: '.+', }, { name: 'gaia_filename', type: 'string', realtype: 'filename', pattern: '.+', }, { name: 'encrypt', type: 'string', realtype: 'boolean', pattern: BOOLEAN_PATTERN, }, { name: 'sign', type: 'string', realtype: 'boolean', pattern: BOOLEAN_PATTERN, }, ], minItems: 4, maxItems: 6, help: 'Put a file into a given Gaia hub, authenticating with the given app private key. ' + 'Optionally encrypt and/or sign the data with the given app private key. If the file is ' + 'successfully stored, this command prints out the URLs at which it can be fetched.\n' + '\n' + 'Gaia is a key-value store, so it does not have any built-in notion of directories. However, ' + 'most underlying storage systems do -- directory separators in the name of a file in ' + "Gaia may be internally treated as first-class directories (it depends on the Gaia hub's driver)." + 'As such, repeated directory separators will be treated as a single directory separator by this command. ' + 'For example, the file name `a/b.txt`, `/a/b.txt`, and `///a////b.txt` will be treated as identical.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # Store 4 versions of a file: plaintext, encrypted, signed, and encrypted+signed\n' + ' $ # Tip: You can obtain the app key with the get_app_keys command.\n' + ' $ export APP_KEY="3ac770e8c3d88b1003bf4a0a148ceb920a6172bdade8e0325a1ed1480ab4fb19"\n' + ' $ stx gaia_putfile https://hub.blockstack.org "$APP_KEY" /path/to/file.txt file.txt\n' + ' {\n' + ' "urls": "https://gaia.blockstack.org/hub/19KAzYp4kSKozeAGMUsnuqkEGdgQQLEvwo/file.txt"\n' + ' }\n' + ' $ stx gaia_putfile https://hub.blockstack.org "$APP_KEY" /path/to/file.txt file-encrypted.txt 1\n' + ' {\n' + ' "urls": "https://gaia.blockstack.org/hub/19KAzYp4kSKozeAGMUsnuqkEGdgQQLEvwo/file-encrypted.txt"\n' + ' }\n' + ' $ stx gaia_putfile https://hub.blockstack.org "$APP_KEY" /path/to/file.txt file-signed.txt 0 1\n' + ' {\n' + ' "urls": "https://gaia.blockstack.org/hub/19KAzYp4kSKozeAGMUsnuqkEGdgQQLEvwo/file-signed.txt"\n' + ' }\n' + ' $ stx gaia_putfile https://hub.blockstack.org "$APP_KEY" /path/to/file.txt file-encrypted-signed.txt 1 1\n' + ' {\n' + ' "urls": "https://gaia.blockstack.org/hub/19KAzYp4kSKozeAGMUsnuqkEGdgQQLEvwo/file-encrypted-signed.txt"\n' + ' }\n' + '```\n', group: 'Gaia', }, gaia_deletefile: { type: 'array', items: [ { name: 'gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'app_private_key', type: 'string', realtype: 'private_key', pattern: PRIVATE_KEY_UNCOMPRESSED_PATTERN, }, { name: 'gaia_filename', type: 'string', realtype: 'filename', pattern: '.+', }, { name: 'was_signed', type: 'string', realtype: 'boolean', pattern: BOOLEAN_PATTERN, }, ], minItems: 3, maxItems: 4, help: 'Delete a file in a Gaia hub, as well as its signature metadata (which is stored in a separate file).' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # Tip: You can obtain the app key with the get_app_keys command.\n' + ' $ export APP_KEY="3ac770e8c3d88b1003bf4a0a148ceb920a6172bdade8e0325a1ed1480ab4fb19"\n' + ' $ stx gaia_deletefile https://hub.blockstack.org "$APP_KEY" file.txt false\n' + ' ok' + '```\n', group: 'Gaia', }, gaia_listfiles: { type: 'array', items: [ { name: 'gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'app_private_key', type: 'string', realtype: 'private_key', pattern: PRIVATE_KEY_UNCOMPRESSED_PATTERN, }, ], minItems: 2, maxItems: 3, help: 'List all the files in a Gaia hub bucket. You must have the private key for the bucket ' + 'in order to list its contents. The command prints each file name on its own line, and when ' + 'finished, it prints the number of files listed.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # Tip: You can obtain the app key with the get_app_keys command.\n' + ' $ export APP_KEY="3ac770e8c3d88b1003bf4a0a148ceb920a6172bdade8e0325a1ed1480ab4fb19"\n' + ' $ stx gaia_listfiles "https://hub.blockstack.org" "$APP_KEY"\n' + ' hello_world\n' + ' dir/format\n' + ' /.dotfile\n' + ' 3\n' + '```\n', group: 'Gaia', }, gaia_restore_bucket: { type: 'array', items: [ { name: 'name_or_id_address', type: 'string', realtype: 'name_or_id_address', pattern: `${ID_ADDRESS_PATTERN}|${NAME_PATTERN}|${SUBDOMAIN_PATTERN}`, }, { name: 'app_origin', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, { name: 'dump_dir', type: 'string', realtype: 'path', pattern: '.+', }, ], minItems: 5, maxItems: 5, help: 'Upload the contents of a previously-dumped Gaia bucket to a new Gaia hub. The `GAIA_HUB` argument ' + 'must correspond to the *write* endpoint of the Gaia hub -- that is, you should be able to fetch ' + '`$GAIA_HUB/hub_info`. `DUMP_DIR` must contain the file contents created by a previous successful run of the gaia_dump_bucket command, ' + 'and both `NAME_OR_ID_ADDRESS` and `APP_ORIGIN` must be the same as they were when it was run.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ export BACKUP_PHRASE="section amount spend resemble spray verify night immune tattoo best emotion parrot"\n' + ' $ stx gaia_restore_bucket hello.id.blockstack https://sample.app https://new.gaia.hub "$BACKUP_PHRASE" ./backups\n' + ' Uploaded ./backups/hello_world to https://new.gaia.hub/hub/1Lr8ggSgdmfcb4764woYutUfFqQMjEoKHc/hello_world\n' + ' Uploaded ./backups/dir\\x2fformat to https://new.gaia.hub/hub/1Lr8ggSgdmfcb4764woYutUfFqQMjEoKHc/dir/format\n' + ' Uploaded ./backups/\\x2f.dotfile to https://new.gaia.hub/hub/1Lr8ggSgdmfcb4764woYutUfFqQMjEoKHc//.dotfile\n' + ' 3\n' + '```\n', group: 'Gaia', }, gaia_sethub: { type: 'array', items: [ { name: 'blockstack_id', type: 'string', realtype: 'blockstack_id', pattern: `^${NAME_PATTERN}|${SUBDOMAIN_PATTERN}$`, }, { name: 'owner_gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'app_origin', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'app_gaia_hub', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, ], minItems: 5, maxItems: 5, help: 'Set the Gaia hub for a particular application for a Blockstack ID. If the command succeeds, ' + 'the URLs to your updated profile will be printed and your profile will contain an entry in its "apps" ' + 'key that links the given `APP_ORIGIN` to the given `APP_GAIA_HUB`.\n' + '\n' + 'NOTE: Both `OWNER_GAIA_HUB` and `APP_GAIA_HUB` must be the *write* endpoints of their respective Gaia hubs.\n' + '\n' + 'Your 12-word phrase (in either raw or encrypted form) is required to re-sign and store your ' + 'profile and to generate an app-specific key and Gaia bucket. If you give the encrypted backup phrase, you will be prompted for a password.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ export BACKUP_PHRASE="soap fog wealth upon actual blossom neither timber phone exile monkey vocal"\n' + ' $ stx gaia_sethub hello_world.id https://hub.blockstack.org https://my.cool.app https://my.app.gaia.hub "$BACKUP_PHRASE"\n' + ' {\n' + ' "profileUrls": {\n' + ' "error": null,\n' + ' "dataUrls": [\n' + ' "https://gaia.blockstack.org/hub/1ArdkA2oLaKnbNbLccBaFhEV4pYju8hJ82/profile.json"\n' + ' ]\n' + ' }\n' + ' }\n' + ' \n' + ' $ # You can check the new apps entry with curl and jq as follows:\n' + ' $ curl -sL https://gaia.blockstack.org/hub/1ArdkA2oLaKnbNbLccBaFhEV4pYju8hJ82/profile.json | jq ".[0].decodedToken.payload.claim.apps"\n' + ' {\n' + ' "https://my.cool.app": "https://my.app.gaia.hub/hub/1EqzyQLJ15KG1WQmi5cf1HtmSeqS1Wb8tY/"\n' + ' }\n' + '```\n' + '\n', group: 'Gaia', }, get_account_history: { type: 'array', items: [ { name: 'address', type: 'string', realtype: 'address', pattern: STACKS_ADDRESS_PATTERN, }, { name: 'page', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, ], minItems: 2, maxItems: 2, help: 'Query the history of account debits and credits over a given block range. ' + 'Returns the history one page at a time. An empty result indicates that the page ' + 'number has exceeded the number of historic operations in the given block range.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ stx get_account_history SP2H7VMY13ESQDAD5808QEY1EMGESMHZWBJRTN2YA 0\n' + ' [\n' + ' {\n' + ' "address": "SP2H7VMY13ESQDAD5808QEY1EMGESMHZWBJRTN2YA",\n' + ' "block_id": 56789\n' + ' "credit_value": "100000000000",\n' + ' "debit_value": "0",\n' + ' "lock_transfer_block_id": 0,\n' + ' "txid": "0e5db84d94adff5b771262b9df015164703b39bb4a70bf499a1602b858a0a5a1",\n' + ' "type": "STACKS",\n' + ' "vtxindex": 0\n' + ' },\n' + ' {\n' + ' "address": "SP2H7VMY13ESQDAD5808QEY1EMGESMHZWBJRTN2YA",\n' + ' "block_id": 56790,\n' + ' "credit_value": "100000000000",\n' + ' "debit_value": "64000000000",\n' + ' "lock_transfer_block_id": 0,\n' + ' "txid": "5a0c67144626f7bd4514e4de3f3bbf251383ca13887444f326bac4bc8b8060ee",\n' + ' "type": "STACKS",\n' + ' "vtxindex": 1\n' + ' },\n' + ' {\n' + ' "address": "SP2H7VMY13ESQDAD5808QEY1EMGESMHZWBJRTN2YA",\n' + ' "block_id": 56791,\n' + ' "credit_value": "100000000000",\n' + ' "debit_value": "70400000000",\n' + ' "lock_transfer_block_id": 0,\n' + ' "txid": "e54c271d6a9feb4d1859d32bc99ffd713493282adef5b4fbf50bca9e33fc0ecc",\n' + ' "type": "STACKS",\n' + ' "vtxindex": 2\n' + ' },\n' + ' {\n' + ' "address": "SP2H7VMY13ESQDAD5808QEY1EMGESMHZWBJRTN2YA",\n' + ' "block_id": 56792,\n' + ' "credit_value": "100000000000",\n' + ' "debit_value": "76800000000",\n' + ' "lock_transfer_block_id": 0,\n' + ' "txid": "06e0d313261baefec1e59783e256ab487e17e0e776e2fdab0920cc624537e3c8",\n' + ' "type": "STACKS",\n' + ' "vtxindex": 3\n' + ' }\n' + ' ]\n' + '```\n' + '\n', group: 'Account Management', }, get_account_at: { type: 'array', items: [ { name: 'address', type: 'string', realtype: 'address', pattern: STACKS_ADDRESS_PATTERN, }, { name: 'blocknumber', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, ], minItems: 2, maxItems: 2, help: 'Query the list of token debits and credits on a given address that occurred ' + 'at a particular block height. Does not include BTC debits and credits; only Stacks.\n' + '\n' + 'Example\n' + '\n' + '```console\n' + ' $ stx -t get_account_at SP2NTAQFECYGSTE1W47P71FG21H8F00KZZWFGEVKQ 56789\n' + ' [\n' + ' {\n' + ' "debit_value": "0",\n' + ' "block_id": 56789\n' + ' "lock_transfer_block_id": 0,\n' + ' "txid": "291817c78a865c1f72938695218a48174265b2358e89b9448edc89ceefd66aa0",\n' + ' "address": "SP2NTAQFECYGSTE1W47P71FG21H8F00KZZWFGEVKQ",\n' + ' "credit_value": "1000000000000000000",\n' + ' "type": "STACKS",\n' + ' "vtxindex": 0\n' + ' }\n' + ' ]\n' + '```\n' + '\n', group: 'Account Management', }, get_address: { type: 'array', items: [ { name: 'private_key', type: 'string', realtype: 'private_key', pattern: `${PRIVATE_KEY_PATTERN_ANY}`, }, ], minItems: 1, maxItems: 1, help: 'Get the address of a private key or multisig private key bundle. Gives the BTC and STACKS addresses\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ stx get_address f5185b9ca93bdcb5753fded3b097dab8547a8b47d2be578412d0687a9a0184cb01\n' + ' {\n' + ' "BTC": "1JFhWyVPpZQjbPcXFtpGtTmU22u4fhBVmq",\n' + ' "STACKS": "SP2YM3J4KQK09V670TD6ZZ1XYNYCNGCWCVVKSDFWQ"\n' + ' }\n' + ' $ stx get_address 1,f5185b9ca93bdcb5753fded3b097dab8547a8b47d2be578412d0687a9a0184cb01,ff2ff4f4e7f8a1979ffad4fc869def1657fd5d48fc9cf40c1924725ead60942c01\n' + ' {\n' + ' "BTC": "363pKBhc5ipDws1k5181KFf6RSxhBZ7e3p",\n' + ' "STACKS": "SMQWZ30EXVG6XEC1K4QTDP16C1CAWSK1JSWMS0QN"\n' + ' }\n' + '```\n', group: 'Key Management', }, get_blockchain_record: { type: 'array', items: [ { name: 'blockstack_id', type: 'string', realtype: 'blockstack_id', pattern: `^${NAME_PATTERN}|${SUBDOMAIN_PATTERN}$`, }, ], minItems: 1, maxItems: 1, help: 'Get the low-level blockchain-hosted state for a Blockstack ID. This command ' + 'is used mainly for debugging and diagnostics. You should not rely on it to be stable.', group: 'Querying Blockstack IDs', }, get_blockchain_history: { type: 'array', items: [ { name: 'blockstack_id', type: 'string', realtype: 'blockstack_id', pattern: `${NAME_PATTERN}|${SUBDOMAIN_PATTERN}$`, }, { name: 'page', type: 'string', realtype: 'page_number', pattern: '^[0-9]+$', }, ], minItems: 1, maxItems: 2, help: 'Get the low-level blockchain-hosted history of operations on a Blockstack ID. ' + 'This command is used mainly for debugging and diagnostics, and is not guaranteed to ' + 'be stable across releases.', group: 'Querying Blockstack IDs', }, get_confirmations: { type: 'array', items: [ { name: 'txid', type: 'string', realtype: 'transaction_id', pattern: TXID_PATTERN, }, ], minItems: 1, maxItems: 1, help: 'Get the block height and number of confirmations for a transaction.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ stx get_confirmations e41ce043ab64fd5a5fd382fba21acba8c1f46cbb1d7c08771ada858ce7d29eea\n' + ' {\n' + ' "blockHeight": 567890,\n' + ' "confirmations": 7,\n' + ' }\n' + '```\n' + '\n', group: 'Peer Services', }, get_namespace_blockchain_record: { type: 'array', items: [ { name: 'namespace_id', type: 'string', realtype: 'namespace_id', pattern: NAMESPACE_PATTERN, }, ], minItems: 1, maxItems: 1, help: 'Get the low-level blockchain-hosted state for a Blockstack namespace. This command ' + 'is used mainly for debugging and diagnostics, and is not guaranteed to be stable across ' + 'releases.', group: 'Namespace Operations', }, get_app_keys: { type: 'array', items: [ { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, { name: 'index', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, { name: 'app_origin', type: 'string', realtype: 'url', pattern: URL_PATTERN, }, ], minItems: 3, maxItems: 3, help: 'Get the application private key from a 12- or 24-word Secret Key and an index of the enumerated associated accounts. ' + 'This is the private key used to sign data in Gaia, and its address is the Gaia bucket ' + 'address. If you provide your encrypted backup phrase, you will be asked to decrypt it. ' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ export BACKUP_PHRASE="one race buffalo dynamic icon drip width lake extra forest fee kit"\n' + ' $ stx get_app_keys "$BACKUP_PHRASE" 1 https://my.cool.dapp\n' + ' {\n' + ' "keyInfo": {\n' + ' "privateKey": "TODO",\n' + ' "address": "TODO"\n' + ' },\n' + ' }\n' + '```\n', group: 'Key Management', }, get_owner_keys: { type: 'array', items: [ { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, { name: 'index', type: 'string', realtype: 'integer', pattern: '^[0-9]+$', }, ], minItems: 1, maxItems: 2, help: 'Get the list of owner private keys and ID-addresses from a 12-word backup phrase. ' + 'Pass non-zero values for INDEX to generate the sequence of ID-addresses that can be used ' + 'to own Blockstack IDs. If you provide an encrypted 12-word backup phrase, you will be ' + 'asked for your password to decrypt it.\n' + '\n' + 'Example:\n' + '\n' + '```console\n' + ' $ # get the first 3 owner keys and addresses for a backup phrase\n' + ' $ export BACKUP_PHRASE="soap fog wealth upon actual blossom neither timber phone exile monkey vocal"\n' + ' $ stx get_owner_keys "$BACKUP_PHRASE" 3\n' + ' [\n' + ' {\n' + ' "privateKey": "14b0811d5cd3486d47279d8f3a97008647c64586b121e99862c18863e2a4183501",\n' + ' "version": "v0.10-current",\n' + ' "index": 0,\n' + ' "idAddress": "ID-1ArdkA2oLaKnbNbLccBaFhEV4pYju8hJ82"\n' + ' },\n' + ' {\n' + ' "privateKey": "1b3572d8dd6866828281ac6cf135f04153210c1f9b123743eccb795fd3095e4901",\n' + ' "version": "v0.10-current",\n' + ' "index": 1,\n' + ' "idAddress": "ID-18pR3UpD1KFrnk88a3MGZmG2dLuZmbJZ25"\n' + ' },\n' + ' {\n' + ' "privateKey": "b19b6d62356db96d570fb5f08b78f0aa7f384525ba3bdcb96fbde29b8e11710d01",\n' + ' "version": "v0.10-current",\n' + ' "index": 2,\n' + ' "idAddress": "ID-1Gx4s7ggkjENw3wSY6bNd1CwoQKk857AqN"\n' + ' }\n' + ' ]\n' + '```\n' + '\n', group: 'Key Management', }, get_payment_key: { type: 'array', items: [ { name: 'backup_phrase', type: 'string', realtype: '12_words_or_ciphertext', }, ], minItems: 1, maxItems: 1, help: 'Get the payment private key from a 12-word backup phrase. If you provide an ' + 'encrypted backup phrase, you will be asked for your password to decrypt it. This command ' + 'will tell you your Bitcoin and Stacks token addresses as well.\n' + '\n' + 'Example\n' + '\n' + '```console\n' + ' $ stx get_payment_key "soap fog wealth upon actual blossom neither timber phone exile monkey vocal"\n' + ' [\n' + ' {\n' + ' "privateKey": "4023435e33da4aff0775f33e7b258f257fb20ecff039c919b5782313ab73afb401",\n' + ' "address": {\n' + ' "BTC": "1ybaP1gaRwRSWRE4f8JXo2W8fiTZmA4rV",\n' + ' "STACKS": "SP5B89ZJAQHBRXVYP15YB5PAY5E24FEW9K4Q63PE"\n' + ' },\n' + ' "index": 0\n' + ' }\n' + ' ]\n' + '```\n' + '\n', group: 'Key Management', }, get_stacks_wallet_key: { type: 'array', items: [ { name: 'backup_phrase', type: 'string',