/api/transactions/locker/download

GET

Download CloudCoins from a RAIDA locker using a 32-character hexadecimal 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 to the active wallet's Import folder. This endpoint implements a secure two-phase protocol:

  1. 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.
  2. 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 saved to the Import/ folder with their POWN strings embedded in the filename, indicating which RAIDAs successfully transferred each coin.

⚠️ Ownership Transfer

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.

💡 Use Case

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
🔐 Security Model

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 one query parameter:

Parameter Type Required Description
locker_key string Yes 32 hexadecimal characters (16 bytes) representing the locker password. Must match the key used when uploading coins to the locker.

Parameter Validation

  • Must be exactly 32 characters long
  • Must contain only hexadecimal characters (0-9, a-f, A-F)
  • Case-insensitive (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

status string
Always "success" for successful downloads.
operation string
Always "locker_download".
coins_found integer
Number of coins found in the locker during Peek phase.
coins_saved integer
Number of coins successfully saved to the Import folder.
destination string
Always "Import" - the folder where downloaded coins are saved.
locker_key string
The locker key used for download (echoed back for confirmation).

Success Response Example

{
  "status": "success",
  "operation": "locker_download",
  "coins_found": 5,
  "coins_saved": 5,
  "destination": "Import",
  "locker_key": "0123456789abcdef0123456789abcdef"
}

Error Responses

400 Bad Request - Invalid locker_key
{
  "status": "error",
  "error": "Invalid locker_key (must be 32 hex characters)",
  "http_code": 400
}
404 Not Found - Locker not found or wrong key
{
  "status": "error",
  "error": "Locker not found",
  "http_code": 404
}

This error occurs when fewer than 13 RAIDAs successfully peek the locker, indicating either a wrong locker key or the locker doesn't exist.

404 Not Found - Locker is empty
{
  "status": "error",
  "error": "Locker is empty",
  "http_code": 404
}

The locker was found and peeked successfully, but contains no coins.

500 Internal Server Error - Request generation failed
{
  "status": "error",
  "error": "Failed to generate RAIDA requests",
  "http_code": 500
}
500 Internal Server Error - Memory allocation failed
{
  "status": "error",
  "error": "Memory allocation failed",
  "http_code": 500
}
503 Service Unavailable - Insufficient RAIDA responses
{
  "status": "error",
  "error": "Insufficient RAIDA responses",
  "http_code": 503
}

Fewer than 13 RAIDAs responded at all (network connectivity issues). Try again later.

Downloaded Coin Files

Downloaded coins are saved to the active wallet's Import/ folder with 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
📊 POWN String Interpretation
  • 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

After downloading coins from a locker, follow this recommended workflow:

1

Download Coins

Call /api/transactions/locker/download with your locker key. Coins are saved to Import/ folder.

2

Unpack (if needed)

If multi-coin files were downloaded, run /api/coins/unpack to extract individual coins.

3

Authenticate

Run /api/coins/authenticate to verify the downloaded coins with all 25 RAIDAs.

4

Grade

Run /api/coins/grade to automatically sort coins based on POWN strings:

  • Bank: 25/25 passes (fully authenticated)
  • Fracked: 13-24 passes (authentic but damaged)
  • Counterfeit: <13 passes (rejected)

5

Fix Fracked Coins (optional)

If any coins ended up in Fracked folder, run /api/coins/fix to heal them and move to Bank.

Examples

JavaScript (fetch)

const API_HOST = 'http://localhost:8080';

async function downloadFromLocker(lockerKey) {
    try {
        const response = await fetch(
            `${API_HOST}/api/transactions/locker/download?locker_key=${lockerKey}`
        );

        const result = await response.json();

        if (result.status === 'success') {
            console.log(`Downloaded ${result.coins_saved} coins to Import folder`);
            console.log(`Total coins found: ${result.coins_found}`);
            console.log(`Locker key: ${result.locker_key}`);

            // Next: Run authenticate and grade commands
            await authenticateCoins();
            await gradeCoins();
        } else {
            console.error(`Error: ${result.error} (HTTP ${result.http_code})`);
        }
    } catch (error) {
        console.error('Network error:', error);
    }
}

async function authenticateCoins() {
    const response = await fetch(`${API_HOST}/api/coins/authenticate`);
    const result = await response.json();
    console.log('Authentication complete:', result);
}

async function gradeCoins() {
    const response = await fetch(`${API_HOST}/api/coins/grade`);
    const result = await response.json();
    console.log('Grading complete:', result);
}

// Example: Download coins from locker
const lockerKey = '0123456789abcdef0123456789abcdef';
downloadFromLocker(lockerKey);

cURL

# Download coins from locker
curl -X GET "http://localhost:8080/api/transactions/locker/download?locker_key=0123456789abcdef0123456789abcdef"

# Example output:
# {
#   "status": "success",
#   "operation": "locker_download",
#   "coins_found": 5,
#   "coins_saved": 5,
#   "destination": "Import",
#   "locker_key": "0123456789abcdef0123456789abcdef"
# }

# Complete workflow: Download, authenticate, and grade
curl -X GET "http://localhost:8080/api/transactions/locker/download?locker_key=0123456789abcdef0123456789abcdef"
curl -X GET "http://localhost:8080/api/coins/authenticate"
curl -X GET "http://localhost:8080/api/coins/grade"

Go

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

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"`
    Destination string `json:"destination"`
    LockerKey   string `json:"locker_key"`
}

type ErrorResponse struct {
    Status   string `json:"status"`
    Error    string `json:"error"`
    HTTPCode int    `json:"http_code"`
}

func downloadFromLocker(lockerKey string) error {
    url := fmt.Sprintf("%s/api/transactions/locker/download?locker_key=%s",
        ApiHost, lockerKey)

    resp, err := http.Get(url)
    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 %d coins to %s folder\n",
            result.CoinsSaved, result.Destination)
        fmt.Printf("Total coins found: %d\n", result.CoinsFound)
        fmt.Printf("Locker key: %s\n", result.LockerKey)

        // Next steps: authenticate and grade
        authenticateCoins()
        gradeCoins()

        return nil
    } else {
        var errResp ErrorResponse
        if err := json.Unmarshal(body, &errResp); err != nil {
            return err
        }
        return fmt.Errorf("Error %d: %s", errResp.HTTPCode, errResp.Error)
    }
}

func authenticateCoins() error {
    resp, err := http.Get(fmt.Sprintf("%s/api/coins/authenticate", ApiHost))
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    fmt.Println("Authentication complete")
    return nil
}

func gradeCoins() error {
    resp, err := http.Get(fmt.Sprintf("%s/api/coins/grade", ApiHost))
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    fmt.Println("Grading complete")
    return nil
}

func main() {
    lockerKey := "0123456789abcdef0123456789abcdef"

    if err := downloadFromLocker(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:

  1. Send Peek requests to all 25 RAIDAs in parallel
  2. Count successful responses (status = ALL_PASS)
  3. If <13 RAIDAs respond at all → Network error (503)
  4. If <13 successful peeks → Wrong key or no locker (404)
  5. If ≥13 successful peeks → Use first RAIDA's coin list (all should match)
  6. 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.