@stacks/cli
Version:
Stacks command line tool
1,089 lines • 142 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfig = exports.checkArgs = exports.getCommandArgs = exports.CLIOptAsStringArray = exports.CLIOptAsBool = exports.CLIOptAsString = exports.getCLIOpts = exports.printUsage = exports.makeUsageString = exports.makeCommandUsageString = exports.makeAllCommandsHelp = exports.makeAllCommandsList = exports.USAGE = exports.CLI_ARGS = exports.DEFAULT_MAX_ID_SEARCH_INDEX = exports.DEFAULT_CONFIG_TESTNET_PATH = exports.DEFAULT_CONFIG_PATH = exports.BOOLEAN_PATTERN = exports.TXID_PATTERN = exports.SUBDOMAIN_PATTERN = exports.URL_PATTERN = exports.ZONEFILE_HASH_PATTERN = exports.INT_PATTERN = exports.PUBLIC_KEY_PATTERN = exports.PRIVATE_KEY_PATTERN_ANY = exports.PRIVATE_KEY_SEGWIT_P2SH_PATTERN = exports.PRIVATE_KEY_MULTISIG_PATTERN = exports.PRIVATE_KEY_NOSIGN_PATTERN = exports.PRIVATE_KEY_UNCOMPRESSED_PATTERN = exports.PRIVATE_KEY_PATTERN = exports.STACKS_ADDRESS_PATTERN = exports.ID_ADDRESS_PATTERN = exports.ADDRESS_PATTERN = exports.C32_ADDRESS_CHARS = exports.ADDRESS_CHARS = exports.NAMESPACE_PATTERN = exports.NAME_PATTERN = void 0;
const ajv_1 = __importDefault(require("ajv"));
const process = __importStar(require("process"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const fs = __importStar(require("fs"));
exports.NAME_PATTERN = '^([0-9a-z_.+-]{3,37})$';
exports.NAMESPACE_PATTERN = '^([0-9a-z_-]{1,19})$';
exports.ADDRESS_CHARS = '[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{1,35}';
exports.C32_ADDRESS_CHARS = '[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+';
exports.ADDRESS_PATTERN = `^(${exports.ADDRESS_CHARS})$`;
exports.ID_ADDRESS_PATTERN = `^ID-${exports.ADDRESS_CHARS}$`;
exports.STACKS_ADDRESS_PATTERN = `^(${exports.C32_ADDRESS_CHARS})$`;
exports.PRIVATE_KEY_PATTERN = '^([0-9a-f]{64,66})$';
exports.PRIVATE_KEY_UNCOMPRESSED_PATTERN = '^([0-9a-f]{64})$';
exports.PRIVATE_KEY_NOSIGN_PATTERN = `^nosign:${exports.ADDRESS_CHARS}$`;
exports.PRIVATE_KEY_MULTISIG_PATTERN = '^([0-9]+),([0-9a-f]{64,66},)*([0-9a-f]{64,66})$';
exports.PRIVATE_KEY_SEGWIT_P2SH_PATTERN = '^segwit:p2sh:([0-9]+),([0-9a-f]{64,66},)*([0-9a-f]{64,66})$';
exports.PRIVATE_KEY_PATTERN_ANY = `${exports.PRIVATE_KEY_PATTERN}|${exports.PRIVATE_KEY_MULTISIG_PATTERN}|${exports.PRIVATE_KEY_SEGWIT_P2SH_PATTERN}|${exports.PRIVATE_KEY_NOSIGN_PATTERN}`;
exports.PUBLIC_KEY_PATTERN = '^([0-9a-f]{66,130})$';
exports.INT_PATTERN = '^-?[0-9]+$';
exports.ZONEFILE_HASH_PATTERN = '^([0-9a-f]{40})$';
exports.URL_PATTERN = '^http[s]?://.+$';
exports.SUBDOMAIN_PATTERN = '^([0-9a-z_+-]{1,37}).([0-9a-z_.+-]{3,37})$';
exports.TXID_PATTERN = '^([0-9a-f]{64})$';
exports.BOOLEAN_PATTERN = '^(0|1|true|false)$';
const LOG_CONFIG_DEFAULTS = {
level: 'info',
handleExceptions: true,
timestamp: true,
stringify: true,
colorize: true,
json: true,
};
const CONFIG_DEFAULTS = {
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`,
logConfig: Object.assign({}, LOG_CONFIG_DEFAULTS, { level: 'debug' }),
};
exports.DEFAULT_CONFIG_PATH = '~/.blockstack-cli.conf';
exports.DEFAULT_CONFIG_TESTNET_PATH = path.join(os.homedir(), '.blockstack-cli-testnet.conf');
exports.DEFAULT_MAX_ID_SEARCH_INDEX = 256;
exports.CLI_ARGS = {
type: 'object',
properties: {
announce: {
type: 'array',
items: [
{
name: 'message_hash',
type: 'string',
realtype: 'zonefile_hash',
pattern: exports.ZONEFILE_HASH_PATTERN,
},
{
name: 'owner_key',
type: 'string',
realtype: 'private_key',
pattern: `${exports.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: exports.URL_PATTERN,
},
{
name: 'backup_phrase',
type: 'string',
realtype: '12_words_or_ciphertext',
pattern: '.+',
},
{
name: 'profile_gaia_hub',
type: 'string',
realtype: 'url',
pattern: exports.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: `${exports.ADDRESS_PATTERN}|${exports.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: `${exports.ADDRESS_PATTERN}`,
},
{
name: 'stx_address',
type: 'string',
realtype: 'address',
pattern: `${exports.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: `${exports.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: `${exports.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: `${exports.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: `${exports.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: `${exports.ADDRESS_PATTERN}|${exports.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: `${exports.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: `${exports.ADDRESS_PATTERN}|${exports.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: `${exports.ID_ADDRESS_PATTERN}|${exports.NAME_PATTERN}|${exports.SUBDOMAIN_PATTERN}`,
},
{
name: 'app_origin',
type: 'string',
realtype: 'url',
pattern: exports.URL_PATTERN,
},
{
name: 'gaia_hub',
type: 'string',
realtype: 'url',
pattern: exports.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: `${exports.NAME_PATTERN}|${exports.SUBDOMAIN_PATTERN}$`,
},
{
name: 'app_origin',
type: 'string',
realtype: 'url',
pattern: exports.URL_PATTERN,
},
{
name: 'filename',
type: 'string',
realtype: 'filename',
pattern: '.+',
},
{
name: 'app_private_key',
type: 'string',
realtype: 'private_key',
pattern: exports.PRIVATE_KEY_UNCOMPRESSED_PATTERN,
},
{
name: 'decrypt',
type: 'string',
realtype: 'boolean',
pattern: exports.BOOLEAN_PATTERN,
},
{
name: 'verify',
type: 'string',
realtype: 'boolean',
pattern: exports.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: exports.URL_PATTERN,
},
{
name: 'app_private_key',
type: 'string',
realtype: 'private_key',
pattern: exports.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: exports.BOOLEAN_PATTERN,
},
{
name: 'sign',
type: 'string',
realtype: 'boolean',
pattern: exports.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: exports.URL_PATTERN,
},
{
name: 'app_private_key',
type: 'string',
realtype: 'private_key',
pattern: exports.PRIVATE_KEY_UNCOMPRESSED_PATTERN,
},
{
name: 'gaia_filename',
type: 'string',
realtype: 'filename',
pattern: '.+',
},
{
name: 'was_signed',
type: 'string',
realtype: 'boolean',
pattern: exports.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: exports.URL_PATTERN,
},
{
name: 'app_private_key',
type: 'string',
realtype: 'private_key',
pattern: exports.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: `${exports.ID_ADDRESS_PATTERN}|${exports.NAME_PATTERN}|${exports.SUBDOMAIN_PATTERN}`,
},
{
name: 'app_origin',
type: 'string',
realtype: 'url',
pattern: exports.URL_PATTERN,
},
{
name: 'gaia_hub',
type: 'string',
realtype: 'url',
pattern: exports.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: `^${exports.NAME_PATTERN}|${exports.SUBDOMAIN_PATTERN}$`,
},
{
name: 'owner_gaia_hub',
type: 'string',
realtype: 'url',
pattern: exports.URL_PATTERN,
},
{
name: 'app_origin',
type: 'string',
realtype: 'url',
pattern: exports.URL_PATTERN,
},
{
name: 'app_gaia_hub',
type: 'string',
realtype: 'url',
pattern: exports.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: exports.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',
r