Snowblossom Docs
Docs › Running software › Wallet & client

Wallet & client

Wallets are deliberately separate from the node: a node is a public database, a wallet holds keys. SnowBlossomClient is the command-line wallet - substantially extended in 2.2 with coin control, on-demand quantum-resistant addresses and offline signing - and the engine behind the JSON-RPC server; IceLeaf wraps the same code in a desktop GUI. Both talk to any node - ideally your own - and verify what the node tells them against the UTXO root in the block headers.

Configuration

KeyDefaultMeaning
wallet_pathrequiredA directory holding the wallet files; created on first run. Back it up.
networksnowblossomWallet files are bound to a network; loading under another fails.
node_uri / node_host, node_port / node_seedbuilt-in seedsWhich node to use; see connecting to a node.
key_modeseedseed, standard, qhard, pqc1, pqc_lite - what new addresses are made of (key modes). Set before creating the wallet.
key_count8Keys generated when a brand-new non-seed wallet is created.
key_pool_size10Number of unused addresses to keep ready; topped up on each run. (Older docs say 100; the code says 10.)
seed_gap20BIP-44 gap: keys are derived up to the highest used index + 20.
watch_onlyfalseWallet without private keys; only xpubs / imported addresses. Cannot create keys or sign.
rpc_port, rpc_host, rpc_username, rpc_password- / localhost / - / -For the rpcserver command; username and password are required (HTTP basic auth).
audit_log_sourceAddress used by the audit-log commands.
log_config_fileLogging properties.
# client.conf
network=snowblossom
log_config_file=configs/logging.properties
node_host=localhost
wallet_path=wallet

Commands

SnowBlossomClient <config> <command> [arguments] [--options]. The command line was overhauled in 2.2: every command now takes named --options (with typo suggestions for near-misses), help lists everything, help <command> - or --help on any command - shows every option, and all commands that existed before still work with exactly the arguments they took before. Exit status is 0 on success, 1 on any error. Before most commands the client runs maintainKeys: it tops up the key pool, derives gap keys and marks addresses as used when the node reports history or a balance for them.

Looking around

CommandDoes
balance [--all] [--quantum] [--json]Balance per address (with the key type of each) and in total. --all includes empty addresses. Balances of all addresses are fetched in parallel now, so large wallets answer quickly.
addressesEvery address in the wallet. Filters: --used / --unused, --quantum, --type falcon (key types containing a string); --balance looks up each balance, --limit N, --json.
outputsEvery unspent output with the output id used for coin control (below). Filters: --from ADDR, --min/--max, --shard N, --confirmed-only, --quantum; --sort value|value-desc|address|tx, --limit N, --ids (bare ids for scripting), --json.
historyTransactions touching this wallet, newest first. Needs a node with addr_index - and says so when the node lacks it instead of pretending the history is empty.
tx <txid> [--raw] [--json]One transaction: inputs, outputs, fee and confirmation state. --raw prints hex that signtx/broadcast accept.
feesThe node's current fee estimate and what a typical send costs at it.
shardsWhich shards exist and where this wallet's coins sit.
walletinfoKeys, addresses, wallet format version - and how many keys the seed words alone can not restore.
checkaddress <addr>…Is this address valid, is it for this network, and is it ours?
nodestatus · version · monitorNode status JSON; client/wallet-format/node versions; watch the balance and print incoming/outgoing transactions as they appear.

Receiving

getfresh [mark_used] [generate_now]An unused address from the key pool. mark_used reserves it (never handed out again, never used for change); generate_now makes a brand-new key instead of taking a pooled one. The named forms --mark-used/--now work too.
newaddress [--key-mode MODE] [--count N]Mint brand-new addresses of a chosen kind - seed, standard, qhard, pqc1 or pqc_lite - in any wallet, without touching the config. --quantum is short for --key-mode pqc_lite. A deliberately minted address is reserved (marked used) so it never shows up as the change of an unrelated send; add --pool to leave it in the fresh pool instead. See key modes.
keymodesExplains each key algorithm, its size and whether the seed can restore it.
pubkey [address]Print one of this wallet's public keys as a TYPE:BASE64 token to hand to multisig co-signers. With no address, a fresh key is picked and reserved; --new mints one.
makemultisig <m> <key|self>… [--out FILE]Build a shared M-of-N address from the given keys (self = a fresh key of this wallet), add it to this wallet and write a share file the other participants import. See below.

Spending

send <amount> <dest>Send coins, with the full option set below. send --to snow:alice=1.5 --to snow:bob=0.25 pays several recipients in one transaction (ADDR:AMOUNT is accepted too). send all <dest> sweeps everything.
sweep <dest> [filters]Send everything - or everything matching --from/--input/--min/--max/--shard - to one address. sweep snow:dest --from snow:old drains a single address.
consolidateMerge many small outputs into one (fewer, larger outputs make later sends smaller and cheaper). With no filters it only takes outputs of 1 SNOW or less and says so; --max, --from, --to ADDR (default: a fresh own address) and --max-inputs (default 100 per round) steer it.
sendlocked <amount> <dest> <fbo> <block> [user|channel <name>]Time-locked / for-benefit-of / name-claiming output (special outputs).

Options shared by send, sweep and consolidate:

OptionMeaning
--from ADDRSpend only outputs sitting on these addresses (repeatable, or comma-separated). A candidate list: the wallet takes just enough unless you add --input-all.
--input TXID:IDXSpend exactly these outputs (see coin control below). Every named output is consumed; the remainder returns as change.
--min / --max / --max-inputs N / --shard N / --confirmed-onlyRestrict which outputs are considered.
--change ADDRSend the change to a specific address. An address that is not in this wallet is refused - the change is usually most of what a transaction moves - unless you add --change-foreign.
--change-mode MODEfresh (default), random or specific (with --change).
--split-change AMTBreak the change into pieces of at most AMT.
--fee AMOUNTFlat fee instead of the node's estimate, in SNOW; append f for flakes (--fee 800f). A fee that looks like a unit slip (over max(1 SNOW, 10 % of the amount)) or a zero fee is refused unless you add --force-fee.
--extra TEXT / --extra-hex HEXAttach up to 100 bytes to the transaction.
--dry-runBuild and display the transaction; send nothing.
--out FILEWrite the transaction to a file instead of broadcasting (offline signing, below). On a watch-only wallet this writes an unsigned transaction and the run succeeds - that is step one of the offline flow.
--yes / --jsonSkip the confirmation prompt; machine-readable output (one JSON document on stdout, notices on stderr).
Confirmation before broadcastingSpending commands show the transaction and ask [y/N] before sending. Without a terminal (cron, pipes) they refuse to broadcast - except the bare legacy form send <amount> <dest> with no options, so existing scripts keep working - unless --yes is given. Amounts are parsed exactly with decimal arithmetic (send 8.2 sends 8,200,000 flakes; older clients sent 8,199,999) and always print with a . regardless of the system locale.

Transactions by hand

decodetx <file|hex|->Show what a raw transaction does - inputs, outputs, fee - marking which of them belong to this wallet, so you can see whose coins a file would move before signing it.
signtx <file|hex|-> [--out FILE] [--broadcast]Add this wallet's signatures (asks first; --yes skips). --broadcast sends it once fully signed.
broadcast <file|hex|->Send raw transaction(s) to the network. A file may hold several, one hex string per line.

Wallet management

export <file> / import <file>Write / merge the wallet as protobuf-JSON. Exports contain private keys and seeds in clear text. Import verifies every key by signing a test hash first.
export_watch_only <file>Like export, with keys and seeds stripped - the input for a watch-only wallet.
import_seed / import_xpub <xpub> / show_seedSeed and xpub handling, unchanged (see below).
backup <directory>Merges the wallet fragments into one file, copies every wallet file to the directory with owner-only permissions, then verifies the copy: loads it and signs a test hash with every key. The safe way to back up non-seed (qhard/PQC) keys.
rpcserverStarts the JSON-RPC server. Deliberately a command, not a config flag.
audit_log_init/record/reportTimestamped message chain on the blockchain (below).

Balance terms: confirmed = outputs in the current UTXO set (even if a mempool transaction spends them); pending = unconfirmed incoming minus unconfirmed outgoing; spendable = everything not spent in the mempool, including unconfirmed change.

Coin control

outputs names every unspent output with an id of the form txid:index:

OUTPUT                      VALUE SHD STATUS KEY    ADDRESS
8b3b58a88b91:0          10.000000 0   ok     ECDSA  snow:5n6798lzza...

That id is what --input takes. The transaction id may be shortened to any unambiguous prefix of at least 6 hex characters - the same habit git gives you - and a bare txid with no :index selects every output of that transaction that belongs to the wallet. Ambiguous prefixes and unknown ids are refused with a pointer to outputs.

# spend one particular output
SnowBlossomClient client.conf send 1.5 snow:dest --input 8b3b58a88b91:0
# spend only what sits on one address
SnowBlossomClient client.conf send 1.5 snow:dest --from snow:myoldaddress
# empty one address completely
SnowBlossomClient client.conf sweep snow:dest --from snow:myoldaddress
# tidy up dust
SnowBlossomClient client.conf consolidate --max 0.5

Semantics worth knowing: --input spends all named outputs (coin control means these coins move); --from is a candidate list unless --input-all is added. Left alone, coin selection now prefers confirmed outputs over unconfirmed ones (this preference was accidentally random in earlier releases), never uses the same outpoint twice, and sends the change to the shard the inputs came from rather than always to shard 0 - which matters on split networks, where shard-0 change from an even shard would immediately be exported away.

Offline signing

Any spending command can write its transaction to a file instead of broadcasting; signtx adds signatures wherever the keys live; broadcast needs no keys at all:

# online machine, watch-only wallet (no keys): build the transaction
SnowBlossomClient online.conf send 1 snow:dest --out tx.hex
# offline machine with the keys: inspect, then sign
SnowBlossomClient offline.conf decodetx tx.hex
SnowBlossomClient offline.conf signtx tx.hex --out signed.hex
# back online: broadcast
SnowBlossomClient online.conf broadcast signed.hex

A partially signed transaction keeps its id (signatures are outside the hash). A send from a wallet that cannot fully sign never broadcasts: with --out it writes the unsigned transaction and succeeds, without it it tells you to add --out for the other signers. signtx validates the transaction after adding its signatures (an unsigned transaction cannot pass the signature checks yet); once fully signed it must pass or the run fails. The same flow serves multisig: pass the file through each signer's signtx until all_signed.

Multi-party multisig

An address is an M-of-N spec (Addresses), so paying one is indistinguishable from any other send, and the single-wallet modes (qhard, pqc1) sign automatically. A multisig between people takes three commands:

# each co-signer prints a key to share (a fresh key, reserved for this purpose):
SnowBlossomClient their.conf pubkey
#   ECDSA:AqLFXrqeWcazdZHgm2Wtgobdc1m0CN7THfGI8o6YxV6R

# one person assembles the address ('self' contributes a fresh key of this wallet):
SnowBlossomClient mine.conf makemultisig 2 self ECDSA:AqLFXrqe... --out share.json
#   Multisig address: snow:6rtfxl8zke23wssw52h3qpvy2kepmw3lalfcyc60
#   Spec:             2of2 ECDSA ECDSA

# every other participant loads the share file - and must see the SAME address:
SnowBlossomClient their.conf import share.json

The share file is an ordinary wallet fragment containing only the public spec (no private keys); it also marks the address as used so no participant's wallet ever picks it as change. Key order matters - the same keys in a different order are a different address - so everyone imports the identical file. Any mix of algorithms is allowed (makemultisig 2 self FALCON:…), and help multisig recaps the whole flow.

Spending is the offline-signing flow above: one wallet builds and part-signs (send <amt> <dest> --from <multisig addr> --out tx.hex), each further signer runs signtx tx.hex --out tx2.hex, and anyone broadcasts the fully signed file. With M < N, any M signers suffice.

Scripting

  • Exit status: 0 on success, 1 on any error (including a rejected broadcast).
  • --json on the read commands and the spending commands prints exactly one JSON document on stdout; human notices go to stderr.
  • Unattended runs: a spending command without a terminal needs --yes (the bare send <amount> <dest> form excepted, for backward compatibility).
  • Amount syntax everywhere: plain decimals are SNOW (1.5, .25); append f/flakes for flakes, snow is accepted explicitly; scientific notation and locale commas are rejected rather than misread.
# every output under 1 SNOW, as ids
SnowBlossomClient client.conf outputs --json | jq -r '.outputs[] | select(.value_flakes < 1000000) | .id'
# feed dust directly into a send
SnowBlossomClient client.conf outputs --ids --max 0.5 | paste -sd,   # then: send ... --input <that list>

Wallet files

wallet_path is a directory of append-only fragments named snow-<version>_<14 hex>.wallet (current format version 5; a legacy wallet.db is still read). Every change - a new key, an address marked used - is written as a small new file; on the next start all files are merged in memory, written as one new file, and the old ones deleted only after the merged file is safely on disk. Files with a newer version than the client understands are merged but never deleted, so an old client cannot destroy data a new one wrote.

Consequences worth knowing:

  • Backup = copy the directory - or better, run backup <dir>, which merges the fragments first, copies with owner-only permissions and verifies that the copy loads and every key still signs. For seed wallets the 12 words suffice (show_seed confirms whether every key derives from the seed); for standard, qhard and PQC wallets new keys are not recoverable from anything else, so refresh the backup after the wallet generates new addresses (each send uses a fresh change address).
  • Syncing is safe. The same directory can be shared between machines with rsync, Dropbox, Unison or a network share; conflicting writes just produce more fragments that merge on the next start. Never use rsync --delete.
  • Not encrypted. Seeds are stored as plain text phrases and private keys as PKCS#8 bytes. A password-based container (PasswordCrypt: scrypt + AES-128-CBC) exists in the code but is not wired into any wallet path. Protect the directory with filesystem permissions or full-disk encryption.
  • A wallet remembers its network; used addresses are keyed by their full string including the prefix.
message WalletDatabase {
  repeated WalletKeyPair keys = 1;          repeated AddressSpec addresses = 2;
  map<string,bool> used_addresses = 4;      map<string,int64> address_create_time = 5;
  int32 version = 10;                       string network = 11;
  map<string,SeedStatus> seeds = 12;        map<string,SeedStatus> xpubs = 13;
}

Seeds and HD wallets

  • Default mode. The phrase is BIP-39 with the standard English word list (the client verifies the list's SHA-256); the wallet generates 12 words (128 bits) and can import 12, 18 or 24. The passphrase is always empty.
  • Derivation is BIP-32/BIP-44 on secp256k1 (bitcoinj): m / 44' / coin' / 0' / 0 / index with coin 2338 on mainnet, 2339 on testnet and regtest, 2340 on the shard test networks. Only external chain 0 is used; change goes to the next unused external address.
  • Each key becomes a 1-of-1 ECDSA_COMPRESSED address. The wallet stores hd_path (e.g. M/44H/2338H/0H/0/7) and a seed_id (BIP-32 identifier of the account key) linking keys to their seed or xpub.
  • The account xpub (m/44'/coin'/0', Bitcoin mainnet version bytes) is printed by show_seed and can be imported elsewhere as a watch-only wallet.
  • Recovery: after import_seed the client derives the first pool of addresses, asks the node for balances and history, marks what is used, extends by the gap and repeats until stable. This needs a node with addr_index to find used addresses with no remaining balance; funds on addresses within the first 10 + 20 indexes are found regardless.
  • Only the first seed in a wallet receives new pool keys; additional imported seeds are only gap-extended.

Watch-only wallets

Two ways to get a wallet that can see but not spend:

  1. From an xpub: a config with watch_only=true, then import_xpub xpub6…. Addresses are derived from the xpub up to the gap limit; new ones appear automatically as earlier ones are used.
  2. From any wallet: export_watch_only watch.txt on the hot wallet and import watch.txt into the watch_only=true wallet (repeat when the source wallet makes new addresses; import_wallet over RPC does it without a restart).

A watch-only wallet can build unsigned transactions (create_transaction with sign=false), which a wallet with keys signs (sign_transaction) and either broadcasts - the offline-signing flow described in the integration guide.

IceLeaf (GUI)

java -jar IceLeaf_deploy.jar (or the Windows installer, which bundles a JDK). A Swing application with tabs:

Wallets
Every wallet in the wallet directory with balance, a fresh address and a “Details” button (seeds, xpubs, recoverability warning).
Send
Destination, amount (or all) and an optional public note (extra). Press once to build and review, a six-second countdown, press again to broadcast.
Receive
QR code and text of the oldest unused address.
Addresses / History
All addresses with balances and used flags; transaction history (needs a node with addr_index, otherwise it says so).
Make Wallet
HD Seed (default; shows the phrase once), Old Standard, QHard, PQC1, PQC Lite, Import seed, Import xpub (watch only).
Node Selection
Local node, the curated TLS seed list, plaintext fallback seeds, or your own list; the fastest responder wins, re-evaluated every five minutes.
Node
Optionally runs a full node inside IceLeaf (RocksDB, indexes on by default; 64-bit Java). It need not run continuously - it catches up quickly when started.
Settings
Wallet directory, node options; most changes need a restart.

Wallets live under ~/.snowblossom/wallets/<name>/ (Linux/macOS) or %APPDATA%\snowblossom\wallets\<name>\ (Windows), each with a wallet.conf (network, key_mode) and a db/ directory of wallet files; testnet uses ~/.snowblossom-teapot. The CLI can open the same wallet by pointing wallet_path at that db/ directory with the same key_mode. Preferences are stored with java.util.prefs. IceLeaf has no mining and no password lock.

Other client tools

Audit log
Set audit_log_source to one of your addresses. audit_log_init <msg> sends the wallet's funds to that address with extra = msg; each audit_log_record <msg> spends the previous record's output back to the same address with a new message, forming a chain of timestamped statements that audit_log_report <address> reconstructs from the node's address history.
VanityGen / HoleGen
See Programs.
MonitorTool
Library class behind monitor: subscribes with SubscribeAddressUpdates and replays history plus mempool on every update.
LoadTest
Creates a stream of random transactions (1–n outputs, change split into 25 SNOW chunks) - used to push testnet into a shard split.