/api/raida/lockers/peek

GET

View the contents of a CloudCoin locker without removing coins. Non-destructive read-only operation.

Description

The /api/raida/lockers/peek endpoint allows you to view the contents of a CloudCoin locker without transferring ownership of the coins. This is a non-destructive operation - coins remain in the locker after peeking.

The endpoint queries all 25 RAIDA servers in parallel using the Locker Peek command (0x0853) and requires cloud consensus (13+ successful responses) to return the coin list. This ensures the locker exists and the key is correct before any destructive operations.

💡 Use Cases

This endpoint is perfect for:

  • Pre-download verification: Confirm locker key is correct before downloading coins
  • Inventory checking: View locker contents without removing coins
  • Upload confirmation: Verify coins were uploaded successfully
  • Sharing with recipient: Show locker contents before transfer
  • Balance verification: Calculate total value before download
⚠️ Peek vs Download
  • Peek (0x0853): View-only operation, coins remain in locker, can be called multiple times
  • Download (0x0854): Transfers ownership, coins removed from locker, destructive operation

Use /api/raida/lockers/peek to verify before using /api/transactions/locker/download.

Parameters

This endpoint requires one query parameter:

Query Parameters

locker_key string (required)
The locker access key as a 32-character hexadecimal string (16 bytes). This is the password to access the locker.
Example: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6

RAIDA Protocol

This endpoint uses the RAIDA Locker Peek command (0x0853) to query locker contents:

Command Details

Command Code 0x0853
Locker Peek - Query locker contents
Request Format bytes
Challenge(16) + LockerKey(16) + Terminator(2: 0x3E3E)
Response Format bytes
[Denomination(1) + SerialNumber(4)] × N + Terminator(2: 0x3E3E)
Authentication none
No coin authentication needed - locker_key is the password
Timeout 10 seconds
Total timeout for all 25 parallel RAIDA requests

Cloud Consensus

The endpoint requires cloud consensus from RAIDA servers to determine if a locker exists:

Consensus Thresholds

Minimum Responses 13/25
At least 13 RAIDAs must respond within timeout period
Minimum Successful Peeks 13/25
At least 13 RAIDAs must return STATUS_ALL_PASS (locker found)
📊 Error Scenarios
  • <13 responses: Network issues (503 Service Unavailable)
  • <13 successful peeks: Wrong key or locker doesn't exist (404 Not Found)
  • 13+ successful peeks: Valid locker, returns coin list (200 OK)

Response

Returns a JSON object with locker contents and RAIDA response details.

Response Properties

status string
Always "success" when locker is found.
operation string
Always "locker_peek".
successful_peeks number
Number of RAIDAs that successfully found the locker (typically 24-25).
responses_received number
Total number of RAIDA responses received within timeout (out of 25).
coin_count number
Total number of coins in the locker.
raida_responses array
Array of 25 objects (one per RAIDA) with response details:
  • raida_id (number): RAIDA server ID (0-24)
  • received (boolean): Whether RAIDA responded
  • status (string): Response status (e.g., "ALL_PASS", "ALL_FAIL")
  • coin_count (number): Number of coins reported by this RAIDA
coins array
Array of coin objects in the locker. Each coin has:
  • denomination (string): Human-readable denomination (e.g., "100", "250")
  • serial_number (number): Unique serial number
  • value (number): Numeric value of the coin

Success Response (200 OK)

{
  "status": "success",
  "operation": "locker_peek",
  "successful_peeks": 24,
  "responses_received": 25,
  "coin_count": 5,
  "raida_responses": [
    {
      "raida_id": 0,
      "received": true,
      "status": "ALL_PASS",
      "coin_count": 5
    },
    {
      "raida_id": 1,
      "received": true,
      "status": "ALL_PASS",
      "coin_count": 5
    },
    // ... 23 more RAIDA responses ...
  ],
  "coins": [
    {
      "denomination": "100",
      "serial_number": 12345,
      "value": 100
    },
    {
      "denomination": "250",
      "serial_number": 67890,
      "value": 250
    },
    {
      "denomination": "25",
      "serial_number": 54321,
      "value": 25
    },
    {
      "denomination": "5",
      "serial_number": 98765,
      "value": 5
    },
    {
      "denomination": "1",
      "serial_number": 13579,
      "value": 1
    }
  ]
}

Error Response - Invalid Locker Key (400 Bad Request)

{
  "status": "error",
  "message": "Invalid locker_key (must be 32 hex characters)",
  "error_code": 400
}

Error Response - Locker Not Found (404 Not Found)

{
  "status": "error",
  "message": "Locker not found",
  "error_code": 404
}

Error Response - Insufficient RAIDA Responses (503 Service Unavailable)

{
  "status": "error",
  "message": "Insufficient RAIDA responses",
  "error_code": 503
}

Examples

JavaScript (fetch)

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

async function peekLocker(lockerKey) {
    try {
        const response = await fetch(
            `${API_HOST}/api/raida/lockers/peek?locker_key=${lockerKey}`
        );

        if (!response.ok) {
            const error = await response.json();
            console.error(`Error ${error.error_code}: ${error.message}`);
            return;
        }

        const result = await response.json();

        console.log(`Locker Peek Results:`);
        console.log(`Successful Peeks: ${result.successful_peeks}/25`);
        console.log(`Responses Received: ${result.responses_received}/25`);
        console.log(`Total Coins: ${result.coin_count}`);
        console.log(`\nCoins in Locker:`);

        let totalValue = 0;
        result.coins.forEach(coin => {
            console.log(`  - ${coin.denomination} CloudCoin #${coin.serial_number}`);
            totalValue += coin.value;
        });

        console.log(`\nTotal Value: ${totalValue} CloudCoins`);
    } catch (error) {
        console.error('Error peeking locker:', error);
    }
}

// Example: Peek a locker
const lockerKey = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6';
peekLocker(lockerKey);

cURL

# Peek a locker to view contents
curl -X GET "http://localhost:8080/api/raida/lockers/peek?locker_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"

# Example with jq to format JSON output
curl -s -X GET "http://localhost:8080/api/raida/lockers/peek?locker_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" | jq .

# Extract just the coin count
curl -s -X GET "http://localhost:8080/api/raida/lockers/peek?locker_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" | jq '.coin_count'

# Extract coin list
curl -s -X GET "http://localhost:8080/api/raida/lockers/peek?locker_key=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" | jq '.coins'

Go

package main

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

const ApiHost = "http://localhost:8080"

type Coin struct {
    Denomination string `json:"denomination"`
    SerialNumber int    `json:"serial_number"`
    Value        int    `json:"value"`
}

type RaidaResponse struct {
    RaidaID   int    `json:"raida_id"`
    Received  bool   `json:"received"`
    Status    string `json:"status"`
    CoinCount int    `json:"coin_count"`
}

type LockerPeekResponse struct {
    Status            string          `json:"status"`
    Operation         string          `json:"operation"`
    SuccessfulPeeks   int             `json:"successful_peeks"`
    ResponsesReceived int             `json:"responses_received"`
    CoinCount         int             `json:"coin_count"`
    RaidaResponses    []RaidaResponse `json:"raida_responses"`
    Coins             []Coin          `json:"coins"`
}

type ErrorResponse struct {
    Status    string `json:"status"`
    Message   string `json:"message"`
    ErrorCode int    `json:"error_code"`
}

func peekLocker(lockerKey string) error {
    url := fmt.Sprintf("%s/api/raida/lockers/peek?locker_key=%s", ApiHost, lockerKey)

    resp, err := http.Get(url)
    if err != nil {
        return fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return fmt.Errorf("read body failed: %w", err)
    }

    if resp.StatusCode != http.StatusOK {
        var errResp ErrorResponse
        if err := json.Unmarshal(body, &errResp); err != nil {
            return fmt.Errorf("decode error failed: %w", err)
        }
        return fmt.Errorf("error %d: %s", errResp.ErrorCode, errResp.Message)
    }

    var result LockerPeekResponse
    if err := json.Unmarshal(body, &result); err != nil {
        return fmt.Errorf("decode response failed: %w", err)
    }

    // Display results
    fmt.Printf("Locker Peek Results:\n")
    fmt.Printf("Successful Peeks: %d/25\n", result.SuccessfulPeeks)
    fmt.Printf("Responses Received: %d/25\n", result.ResponsesReceived)
    fmt.Printf("Total Coins: %d\n\n", result.CoinCount)

    fmt.Println("Coins in Locker:")
    totalValue := 0
    for _, coin := range result.Coins {
        fmt.Printf("  - %s CloudCoin #%d\n", coin.Denomination, coin.SerialNumber)
        totalValue += coin.Value
    }

    fmt.Printf("\nTotal Value: %d CloudCoins\n", totalValue)
    return nil
}

func main() {
    lockerKey := "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"

    if err := peekLocker(lockerKey); err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

Related Endpoints

/api/transactions/locker/download

Download coins from a locker and transfer ownership to your wallet. Destructive operation that removes coins from locker.

/api/raida/audit

Count total coins stored across the entire RAIDA network. Public endpoint requiring no authentication.

Implementation Notes

🔧 Technical Details
  • Parallel Execution: All 25 RAIDA requests execute simultaneously for optimal performance
  • Timeout: 10-second total timeout for all parallel requests
  • Production Ready: Fully implemented and tested
  • Read-Only: No modifications to locker contents or coin ownership
  • Idempotent: Can be called multiple times without side effects
  • No Authentication: Locker key serves as both identifier and password
⚠️ Security Considerations
  • Key Confidentiality: Locker keys must be kept secret - anyone with the key can access locker contents
  • HTTPS Recommended: Use HTTPS in production to protect locker keys in transit
  • Key Length: Must be exactly 32 hex characters (16 bytes) - shorter or longer keys will be rejected
  • Rate Limiting: Consider implementing rate limiting to prevent brute-force key guessing

Workflow Example

A typical locker transfer workflow using peek before download:

Complete Workflow

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

async function receiveLockerTransfer(lockerKey) {
    try {
        // Step 1: Peek to verify locker contents
        console.log('Step 1: Peeking locker...');
        const peekResp = await fetch(
            `${API_HOST}/api/raida/lockers/peek?locker_key=${lockerKey}`
        );

        if (!peekResp.ok) {
            const error = await peekResp.json();
            throw new Error(`Peek failed: ${error.message}`);
        }

        const peekData = await peekResp.json();
        console.log(`Found ${peekData.coin_count} coins in locker`);

        // Step 2: Calculate total value
        const totalValue = peekData.coins.reduce((sum, coin) => sum + coin.value, 0);
        console.log(`Total value: ${totalValue} CloudCoins`);

        // Step 3: Confirm with user (in real app, show UI dialog)
        console.log('Confirm download? (yes/no)');
        // Assuming user confirms...

        // Step 4: Download coins from locker
        console.log('Step 2: Downloading coins...');
        const downloadResp = await fetch(
            `${API_HOST}/api/transactions/locker/download?locker_key=${lockerKey}`
        );

        if (!downloadResp.ok) {
            const error = await downloadResp.json();
            throw new Error(`Download failed: ${error.message}`);
        }

        const downloadData = await downloadResp.json();
        console.log(`Successfully downloaded ${downloadData.coins_downloaded} coins`);
        console.log(`Files saved to: ${downloadData.import_folder}`);

        // Step 5: Verify locker is now empty (optional)
        console.log('Step 3: Verifying locker is empty...');
        const verifyResp = await fetch(
            `${API_HOST}/api/raida/lockers/peek?locker_key=${lockerKey}`
        );

        const verifyData = await verifyResp.json();
        if (verifyData.coin_count === 0) {
            console.log('✓ Locker is now empty - transfer complete');
        } else {
            console.warn(`⚠ Warning: ${verifyData.coin_count} coins still in locker`);
        }

    } catch (error) {
        console.error('Transfer failed:', error.message);
    }
}

// Example usage
const receivedLockerKey = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6';
receiveLockerTransfer(receivedLockerKey);