/api/transactions/locker/download
GETDownload CloudCoins from a RAIDA locker using a string locker key. Executes two-phase protocol: Peek to view coins, then Remove to download with new ANs.
Description
The /api/transactions/locker/download endpoint downloads CloudCoins from a RAIDA locker and automatically grades them into the appropriate folder (Bank, Fracked, or Counterfeit). This endpoint implements a secure two-phase protocol:
- Peek Phase (0x0853): Queries all 25 RAIDA servers to retrieve the list of coins stored in the locker (denomination + serial number pairs). Requires 13+ successful responses for cloud consensus.
- Remove Phase (0x0854): Downloads the coins from the locker with newly generated random Authenticity Numbers (ANs). The old locker ANs are discarded, transferring ownership to the downloader.
Downloaded coins are automatically graded and moved to the appropriate folder (Bank, Fracked, or Counterfeit) based on POWN results from the Remove operation.
Important: Downloading coins from a locker is a destructive operation. Once downloaded, the coins are removed from the locker and transferred with new ANs. The sender cannot recover the coins after download.
This endpoint is essential for:
- Receiving CloudCoins sent via locker by another user
- Transferring coins between your own wallets
- Collecting coins from a shared locker after out-of-band key exchange
- Automated locker monitoring and coin retrieval
The locker key is NOT the same as coin ANs. It's a 16-byte password/secret shared out-of-band between sender and receiver. Anyone with the locker key can download the coins, so keep it secret and share only via secure channels.
Parameters
This endpoint requires two query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
wallet_path |
string | Yes | Full path to the wallet where downloaded coins will be deposited. Example: E:/Client_Data/Wallets/Default |
locker_key |
string | Yes | String representing the locker password. Must match the key used when uploading coins to the locker. |
Parameter Validation
wallet_path:
- Must be a valid path to an existing wallet directory
- Use forward slashes (/) for path separators
- URL encode spaces and special characters
locker_key:
- Any length
- Any string. The Core will run an MD5 hash on it.
- Case-insensitive to reduce chances of failure (0A1B2C3D = 0a1b2c3d)
Two-Phase Protocol
Phase 1: Locker Peek (0x0853)
The Peek phase queries all 25 RAIDA servers to retrieve the list of coins in the locker without downloading them.
Request Structure:
- Challenge: 16 bytes (random + CRC32)
- Locker Key: 16 bytes
- Terminator: 0x3E3E (2 bytes)
Response Structure:
- ALL_PASS (0xF1): Returns list of DN+SN pairs for all coins
- ALL_FAIL (0xF2): Wrong locker key or locker doesn't exist
- Error codes: RAIDA server errors
Consensus Requirements:
- Minimum 13 successful Peek responses required (out of 25 RAIDAs)
- If <13 RAIDAs respond: Network issues (503 error)
- If <13 successful peeks: Wrong key or locker doesn't exist (404 error)
Phase 2: Locker Remove (0x0854)
The Remove phase downloads the coins from the locker with newly generated random ANs, transferring ownership.
Request Structure:
- Challenge: 16 bytes (random + CRC32)
- Locker Key: 16 bytes
- For each coin:
- Denomination: 1 byte
- Serial Number: 4 bytes
- New AN: 16 bytes (randomly generated)
- Terminator: 0x3E3E (2 bytes)
Response Structure:
- ALL_PASS (0xF1): All coins successfully removed and updated
- ALL_FAIL (0xF2): All coins failed (wrong key or expired)
- MIXED (0xF3): Some coins succeeded, some failed (bitfield response)
- Error codes: RAIDA server errors
POWN String Update:
Based on each RAIDA's response, the coin's POWN string is updated:
- 'p' (0x0A): Pass - coin successfully downloaded from this RAIDA
- 'f' (0x0F): Fail - authentication failed
- 'e' (0x0E): Error - RAIDA error occurred
- 'n' (0x0C): No reply - timeout
Response
Returns a JSON object with download results and coin details.
Success Response Properties
[wallet]/Receipts/[task_id].json) and as the task tag on the downloaded coin files..json file written under [wallet]/Receipts/. Includes the aggregate totals and a per-coin coins[] array (sorted denomination DESC, then serial number ASC) listing each downloaded coin's serial number, denomination, POWN authentication string ("AAAAA AAAAA AAAAA AAAAA AAAAA" format), and final bucket (Bank/Fracked/Limbo/Counterfeit). See the /api/transactions/deposit reference for the full schema. The per-coin list is capped at 500 coins.Success Response Example
{
"status": "success",
"operation": "locker_download",
"coins_found": 5,
"coins_saved": 5,
"total_value": 1337,
"graded_to_bank": 4,
"graded_to_fracked": 1,
"graded_to_counterfeit": 0,
"wallet_path": "E:/Client_Data/Wallets/Default",
"locker_key": "AAA-BBBB",
"raida_success": 25,
"task_id": "Apr-18-26_05-00-18-PM-a1b2",
"receipt": {
"schema_version": 1,
"type": "locker_download",
"date": "2026-04-18T17:00:18",
"task_id": "Apr-18-26_05-00-18-PM-a1b2",
"memo": null,
"totals": {
"bank_count": 4, "fracked_count": 1,
"limbo_count": 0, "counterfeit_count": 0,
"duplicate_count": 0, "error_count": 0,
"value_bank": 0, "value_fracked": 0,
"value_limbo": 0, "value_counterfeit": 0,
"total_deposited": 1337
},
"locker": { "locker_key": "AAA-BBBB", "raida_consensus": 25 },
"per_coin_detail_truncated": false,
"per_coin_detail_omitted_reason": null,
"coins": [
{ "sn": 1234567890, "denom": 2, "pown": "AAAAA AAAAA AAAAA AAAAA AAAAA", "bucket": "Bank" },
{ "sn": 1234567891, "denom": 2, "pown": "AAAAA AAAAA AAAAF AAAAA AAAAA", "bucket": "Fracked" }
]
}
}
Downloaded coin files are now saved with the receipt's task_id as the filename tag (e.g. coin_1234567890_dn2.Apr-18-26_05-00-18-PM-a1b2.bin). Previously the literal string LockerDownload was used. Any tooling that greps coin filenames for LockerDownload needs updating.
Error Responses
400 Bad Request - Missing locker key
{
"error": true,
"message": "Missing required parameter: locker_key",
"code": 400
}
404 Not Found - Wallet not found
{
"error": true,
"message": "Wallet not found",
"code": 404
}
This is returned before any RAIDA call when the destination wallet directory does not exist.
500 Internal Server Error - Locker or RAIDA failure
{
"error": true,
"message": "Locker download failed",
"code": 500,
"operation": "locker_download",
"locker_key": "AAA-BBBB",
"raida_success": 8,
"raida_status_code": 242,
"detail": "ALL_FAIL"
}
Runtime locker failures now use the repo-wide error envelope and may include detail plus RAIDA metadata.
Downloaded Coin Files
Downloaded coins are automatically graded and saved to the appropriate folder (Bank, Fracked, or Counterfeit) based on POWN results. Files use the following filename format:
{value} CloudCoin #{serial} '{pown_string}'.bin
Filename Components:
- {value}: Coin denomination value (e.g., 1, 5, 25, 100, 250)
- {serial}: Coin serial number (0-16777215)
- {pown_string}: 25-character POWN string showing download status per RAIDA
Example Filenames:
250 CloudCoin #1234567 'ppppppppppppppppppppppppp'.bin
100 CloudCoin #8901234 'pppppppppppppnfpppppppppp'.bin
1 CloudCoin #5678901 'pppppppppppppeepppppppnpp'.bin
- 25 passes (all 'p'): Fully authenticated coin - move to Bank
- 13-24 passes: Authentic but fracked - move to Fracked folder, run Fix command
- <13 passes: Counterfeit or failed - move to Counterfeit folder
Post-Download Workflow
The locker download endpoint now handles grading automatically. After downloading:
Download Coins (Automatic)
Call /api/transactions/locker/download with your locker key. The endpoint automatically:
- Peeks at locker contents
- Downloads coins with new ANs
- Grades coins based on POWN results
- Moves coins to Bank, Fracked, or Counterfeit folders
Fix Fracked Coins (optional)
If any coins ended up in the Fracked folder, run /api/coins/fix to heal them and move to Bank. The auto-healer will also attempt to fix fracked coins periodically.
Unlike the previous version which required manual authentication and grading steps, the updated locker download automatically grades coins based on the Remove operation's POWN results. This reduces the workflow from 5 steps to just 1-2 steps.
Examples
JavaScript (fetch)
const API_HOST = 'http://localhost:8080';
async function downloadFromLocker(walletPath, lockerKey) {
try {
const response = await fetch(
`${API_HOST}/api/transactions/locker/download?wallet_path=${encodeURIComponent(walletPath)}&locker_key=${lockerKey}`
);
const result = await response.json();
if (result.status === 'success') {
console.log(`Downloaded and graded ${result.coins_saved} coins to ${result.wallet_path}`);
console.log(`Total coins found: ${result.coins_found}`);
console.log(`Graded to Bank: ${result.graded_to_bank}`);
console.log(`Graded to Fracked: ${result.graded_to_fracked}`);
console.log(`Graded to Counterfeit: ${result.graded_to_counterfeit}`);
// Optional: Fix fracked coins if any
if (result.graded_to_fracked > 0) {
console.log('Running fix for fracked coins...');
await fixFrackedCoins(walletPath);
}
} else {
console.error(`Error: ${result.message} (HTTP ${response.status})`);
if (result.detail) {
console.error(`Detail: ${result.detail}`);
}
}
} catch (error) {
console.error('Network error:', error);
}
}
async function fixFrackedCoins(walletPath) {
const response = await fetch(`${API_HOST}/api/coins/fix?wallet_path=${encodeURIComponent(walletPath)}`);
const result = await response.json();
console.log('Fix complete:', result);
}
// Example: Download coins from locker to specific wallet
const walletPath = 'E:/Client_Data/Wallets/Default';
const lockerKey = '0123456789abcdef0123456789abcdef';
downloadFromLocker(walletPath, lockerKey);
cURL
# Download coins from locker to specific wallet (automatically grades to Bank/Fracked/Counterfeit)
curl -X GET "http://localhost:8080/api/transactions/locker/download?wallet_path=E:/Client_Data/Wallets/Default&locker_key=0123456789abcdef0123456789abcdef"
# Example output:
# {
# "status": "success",
# "operation": "locker_download",
# "coins_found": 5,
# "coins_saved": 5,
# "graded_to_bank": 4,
# "graded_to_fracked": 1,
# "graded_to_counterfeit": 0,
# "wallet_path": "E:/Client_Data/Wallets/Default",
# "locker_key": "0123456789abcdef0123456789abcdef"
# }
# Optional: Fix fracked coins if any were downloaded
curl -X GET "http://localhost:8080/api/coins/fix?wallet_path=E:/Client_Data/Wallets/Default"
Go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
const ApiHost = "http://localhost:8080"
type LockerDownloadResponse struct {
Status string `json:"status"`
Operation string `json:"operation"`
CoinsFound int `json:"coins_found"`
CoinsSaved int `json:"coins_saved"`
GradedToBank int `json:"graded_to_bank"`
GradedToFracked int `json:"graded_to_fracked"`
GradedToCounterfeit int `json:"graded_to_counterfeit"`
WalletPath string `json:"wallet_path"`
LockerKey string `json:"locker_key"`
}
type ErrorResponse struct {
Error bool `json:"error"`
Message string `json:"message"`
Code int `json:"code"`
Detail string `json:"detail,omitempty"`
}
func downloadFromLocker(walletPath, lockerKey string) error {
reqUrl := fmt.Sprintf("%s/api/transactions/locker/download?wallet_path=%s&locker_key=%s",
ApiHost, url.QueryEscape(walletPath), lockerKey)
resp, err := http.Get(reqUrl)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode == 200 {
var result LockerDownloadResponse
if err := json.Unmarshal(body, &result); err != nil {
return err
}
fmt.Printf("Success! Downloaded and graded %d coins to %s\n", result.CoinsSaved, result.WalletPath)
fmt.Printf("Total coins found: %d\n", result.CoinsFound)
fmt.Printf("Graded to Bank: %d\n", result.GradedToBank)
fmt.Printf("Graded to Fracked: %d\n", result.GradedToFracked)
fmt.Printf("Graded to Counterfeit: %d\n", result.GradedToCounterfeit)
// Optional: Fix fracked coins if any
if result.GradedToFracked > 0 {
fmt.Println("Running fix for fracked coins...")
fixFrackedCoins(walletPath)
}
return nil
} else {
var errResp ErrorResponse
if err := json.Unmarshal(body, &errResp); err != nil {
return err
}
return fmt.Errorf("Error %d: %s (%s)", errResp.Code, errResp.Message, errResp.Detail)
}
}
func fixFrackedCoins(walletPath string) error {
resp, err := http.Get(fmt.Sprintf("%s/api/coins/fix?wallet_path=%s", ApiHost, url.QueryEscape(walletPath)))
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Println("Fix complete")
return nil
}
func main() {
walletPath := "E:/Client_Data/Wallets/Default"
lockerKey := "0123456789abcdef0123456789abcdef"
if err := downloadFromLocker(walletPath, lockerKey); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
Technical Details
RAIDA Protocol
Command Codes:
- 0x0853: Locker Peek - Query locker contents
- 0x0854: Locker Remove - Download and remove coins
Timeout Configuration:
- Per-RAIDA timeout: 10 seconds (10000ms)
- Parallel execution: All 25 RAIDAs queried simultaneously
- Total operation time: ~10-15 seconds (single timeout period)
Cloud Consensus Algorithm:
- Send Peek requests to all 25 RAIDAs in parallel
- Count successful responses (status = ALL_PASS)
- If <13 RAIDAs respond at all → Network error (503)
- If <13 successful peeks → Wrong key or no locker (404)
- If ≥13 successful peeks → Use first RAIDA's coin list (all should match)
- Proceed to Remove phase with consensus coin list
New AN Generation:
For security, new random ANs are generated for each coin during download:
- Each coin gets 25 new 16-byte random ANs (one per RAIDA)
- Random source: BCrypt (Windows) or /dev/urandom (Unix)
- Old locker ANs are discarded and cannot be recovered
- Sender has no knowledge of the new ANs after download
File Format
Binary File Structure (CloudCoin v3, Format 9):
- File Header: 32 bytes
- Format ID: 9 (1 byte)
- Coin ID: 0x0006 (2 bytes)
- Encryption: 0 (1 byte - unencrypted)
- Password hash: 7 bytes (empty for unencrypted)
- POWN string: 12.5 bytes (25 nibbles, 4-bit packed)
- Reserved: 8 bytes
- Token count: 1 (2 bytes - single coin file)
- Coin Header: 7 bytes
- Split: 0 (1 byte)
- Shard: 0 (1 byte)
- Denomination: -8 to +11 (1 byte)
- Serial Number: 0-16777215 (4 bytes)
- Coin Body: 400 bytes
- 25 Authenticity Numbers × 16 bytes each
- Each AN is a random 128-bit value
- Total file size: 439 bytes per coin
Related Endpoints
/api/raida/lockers/peek
Peek at locker contents without downloading. View which coins are available before committing to download.
/api/raida/locker/upload
Upload CloudCoins to a locker for transfer to another user or wallet.
/api/coins/import
Import coins from various file formats (binary, JSON, PNG, JPEG) into the active wallet.
/api/coins/authenticate
Authenticate coins by checking with all 25 RAIDA servers and updating POWN strings.
/api/coins/grade
Sort coins into Bank, Fracked, or Counterfeit folders based on POWN string authentication results.
/api/coins/fix
Heal fracked coins (13-24 passes) using RAIDA ticket-based repair protocol.