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
| Key | Default | Meaning |
|---|---|---|
wallet_path | required | A directory holding the wallet files; created on first run. Back it up. |
network | snowblossom | Wallet files are bound to a network; loading under another fails. |
node_uri / node_host, node_port / node_seed | built-in seeds | Which node to use; see connecting to a node. |
key_mode | seed | seed, standard, qhard, pqc1, pqc_lite - what new addresses are made of (key modes). Set before creating the wallet. |
key_count | 8 | Keys generated when a brand-new non-seed wallet is created. |
key_pool_size | 10 | Number of unused addresses to keep ready; topped up on each run. (Older docs say 100; the code says 10.) |
seed_gap | 20 | BIP-44 gap: keys are derived up to the highest used index + 20. |
watch_only | false | Wallet 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_source | Address used by the audit-log commands. | |
log_config_file | Logging 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
| Command | Does |
|---|---|
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. |
addresses | Every address in the wallet. Filters: --used / --unused, --quantum, --type falcon (key types containing a string); --balance looks up each balance, --limit N, --json. |
outputs | Every 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. |
history | Transactions 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. |
fees | The node's current fee estimate and what a typical send costs at it. |
shards | Which shards exist and where this wallet's coins sit. |
walletinfo | Keys, 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 · monitor | Node 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. |
keymodes | Explains 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. |
consolidate | Merge 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:
| Option | Meaning |
|---|---|
--from ADDR | Spend 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:IDX | Spend 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-only | Restrict which outputs are considered. |
--change ADDR | Send 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 MODE | fresh (default), random or specific (with --change). |
--split-change AMT | Break the change into pieces of at most AMT. |
--fee AMOUNT | Flat 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 HEX | Attach up to 100 bytes to the transaction. |
--dry-run | Build and display the transaction; send nothing. |
--out FILE | Write 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 / --json | Skip the confirmation prompt; machine-readable output (one JSON document on stdout, notices on stderr). |
[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_seed | Seed 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. |
rpcserver | Starts the JSON-RPC server. Deliberately a command, not a config flag. |
audit_log_init/record/report | Timestamped 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).
--jsonon 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 baresend <amount> <dest>form excepted, for backward compatibility). - Amount syntax everywhere: plain decimals are SNOW (
1.5,.25); appendf/flakesfor flakes,snowis 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_seedconfirms 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 / indexwith 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_COMPRESSEDaddress. The wallet storeshd_path(e.g.M/44H/2338H/0H/0/7) and aseed_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 byshow_seedand can be imported elsewhere as a watch-only wallet. - Recovery: after
import_seedthe 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 withaddr_indexto 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:
- From an xpub: a config with
watch_only=true, thenimport_xpub xpub6…. Addresses are derived from the xpub up to the gap limit; new ones appear automatically as earlier ones are used. - From any wallet:
export_watch_only watch.txton the hot wallet andimport watch.txtinto thewatch_only=truewallet (repeat when the source wallet makes new addresses;import_walletover 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_sourceto one of your addresses.audit_log_init <msg>sends the wallet's funds to that address withextra = msg; eachaudit_log_record <msg>spends the previous record's output back to the same address with a new message, forming a chain of timestamped statements thataudit_log_report <address>reconstructs from the node's address history. - VanityGen / HoleGen
- See Programs.
- MonitorTool
- Library class behind
monitor: subscribes withSubscribeAddressUpdatesand 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.