/api/coins/join

GET

Join multiple smaller denomination coins into a single larger denomination coin.

⚠️ PARAMETER VALIDATION ONLY

Current Implementation Status: This endpoint currently validates the target denomination parameter but does not perform the actual coin joining operation. Full implementation is pending integration with cmd_join_coins() to execute the RAIDA Join protocol (0x095d).

Current Behavior: Returns success with validated denomination and value. Coins remain unchanged in wallet.

Planned Implementation: Will automatically select optimal combination of smaller coins, communicate with all 25 RAIDA servers, destroy source coins, and create a single joined coin with new serial number.

Description

The /api/coins/join endpoint will consolidate multiple smaller denomination coins into a single larger denomination coin. This operation is useful for simplifying wallet management and reducing the total number of coins while maintaining the same value.

When fully implemented, the system will:

  1. Select multiple coins from the Bank folder with a total value equal to the target denomination
  2. Request an available serial number from RAIDA for the joined coin
  3. Send a Join (0x095d) request to all 25 RAIDA servers with source coin details
  4. RAIDA destroys the source coins and creates a single joined coin
  5. Download the joined coin with new Authenticity Numbers (ANs)
  6. Save the joined coin to the Bank folder
💡 Use Cases

This endpoint will be perfect for:

  • Wallet consolidation: Reduce the number of individual coins
  • Denomination management: Create specific denominations for payments
  • Balance simplification: Convert many small coins into fewer large ones
  • Transaction preparation: Create exact denominations needed for transfers
🔐 RAIDA Protocol: Join (0x095d)

Protocol Details:

  • Command Code: 0x095d (Group 0x09, Command 0x5d)
  • Operation Type: Atomic multi-coin destruction and single-coin creation
  • Requirements: All 25 RAIDA servers must agree to the join operation
  • Source Coins: Automatically destroyed upon successful join
  • Target Coin: Created with new serial number from RAIDA pool
  • Total Value: Sum of source coins must exactly equal target denomination

Parameters

This endpoint requires one query parameter:

Parameter Type Required Description
denomination integer Yes Target denomination code to join into (0-10). See denomination reference table below.

Denomination Reference

Valid denomination codes and their corresponding CloudCoin values:

Code | Value        | Common Join Examples
-----|--------------|------------------------------------
  0  | 1            | 5× 0.1 → 1, or 2× 0.25 + 2× 0.1 + 2× 0.05 → 1
  1  | 5            | 5× 1 → 5
  2  | 25           | 5× 5 → 25
  3  | 100          | 4× 25 → 100
  4  | 250          | 10× 25 → 250, or 2× 100 + 2× 25 → 250
  5  | 1,000        | 4× 250 → 1,000, or 10× 100 → 1,000
  6  | 5,000        | 2× 1,000 + 2× 1,000 + 1,000 → 5,000
  7  | 25,000       | 5× 5,000 → 25,000
  8  | 100,000      | 4× 25,000 → 100,000
  9  | 250,000      | 10× 25,000 → 250,000
 10  | 1,000,000    | 4× 250,000 → 1,000,000
💡 Automatic Coin Selection

When fully implemented, the system will automatically select the optimal combination of source coins:

  • Exact Match Priority: Prefers combinations that exactly match the target (e.g., 4× 25 for 100)
  • Minimum Coins: Minimizes the number of source coins used
  • Denomination Balance: Avoids leaving odd or difficult-to-use denominations
  • Value Precision: Ensures total source value exactly equals target denomination

Response

Returns a JSON object with join operation status and details.

Response Properties (Current Implementation)

status string
Operation status: "success" or "error".
operation string
Always "coins-join".
message string
Currently: "Join coins endpoint - parameter-based implementation pending". Will describe join result when implemented.
wallet string
Name of the active wallet.
target_denomination integer
The validated target denomination code (0-10).
target_value integer
The CloudCoin value corresponding to the target denomination.
note string
Implementation status note.

Future Response Properties (When Fully Implemented)

source_coins array
Array of source coins that were joined, each with serial_number, denomination, and value.
source_count integer
Total number of source coins joined.
joined_coin object
Details of the newly created joined coin including serial_number, denomination, and new ANs.
raida_results object
RAIDA server responses: total_raidas (25), success_count, fail_count, error_count.
pown_string string
25-character POWN string showing join result per RAIDA (p=pass, f=fail, e=error, n=no reply).
authenticity_status string
Overall result: "authentic" (13+ passes), "fracked" (some failures), or "counterfeit" (join failed).

Example Success Response (Current Implementation)

{
  "status": "success",
  "operation": "coins-join",
  "message": "Join coins endpoint - parameter-based implementation pending",
  "wallet": "Default",
  "target_denomination": 5,
  "target_value": 1000,
  "note": "System will join smaller coins to create this denomination - needs cmd_join_coins() integration"
}

Example Success Response (Future Implementation)

{
  "status": "success",
  "operation": "coins-join",
  "message": "Successfully joined 4 coins into 1 coin of denomination 1000",
  "wallet": "Default",
  "target_denomination": 5,
  "target_value": 1000,
  "source_coins": [
    {
      "serial_number": 12345,
      "denomination": 4,
      "value": 250
    },
    {
      "serial_number": 12346,
      "denomination": 4,
      "value": 250
    },
    {
      "serial_number": 12347,
      "denomination": 4,
      "value": 250
    },
    {
      "serial_number": 12348,
      "denomination": 4,
      "value": 250
    }
  ],
  "source_count": 4,
  "joined_coin": {
    "serial_number": 89012,
    "denomination": 5,
    "value": 1000,
    "file_path": "Bank/1000.CloudCoin.1.89012.stack"
  },
  "raida_results": {
    "total_raidas": 25,
    "success_count": 25,
    "fail_count": 0,
    "error_count": 0
  },
  "pown_string": "ppppppppppppppppppppppppp",
  "authenticity_status": "authentic"
}

Example Error Response (Missing Parameter)

{
  "status": "error",
  "operation": "coins-join",
  "message": "Missing required parameter: denomination"
}

Example Error Response (Invalid Denomination)

{
  "status": "error",
  "operation": "coins-join",
  "message": "Invalid denomination value: 15. Must be between 0 and 10"
}

Example Error Response (No Active Wallet)

{
  "status": "error",
  "operation": "coins-join",
  "message": "No active wallet. Please select a wallet first."
}

Example Error Response (Insufficient Coins - Future Implementation)

{
  "status": "error",
  "operation": "coins-join",
  "message": "Insufficient coins to join into denomination 1000. Need total value of 1000, but only have 750 in available coins."
}

HTTP Status Codes

Status Code Description
200 OK Parameter validation successful (current), or join operation completed successfully (future).
400 Bad Request Missing or invalid 'denomination' parameter, or no active wallet.
500 Internal Server Error Server error during join operation (future implementation).

Join Operation Details (Future Implementation)

Step-by-Step Process

  1. Coin Selection: System scans Bank folder and selects optimal combination of coins whose total value equals the target denomination.
  2. Serial Number Request: Queries RAIDA for an available serial number to assign to the joined coin.
  3. Join Request Construction: Builds RAIDA Join (0x095d) request packet containing:
    • Source coin serial numbers, denominations, and ANs
    • Target denomination code
    • New serial number for joined coin
  4. Multi-threaded RAIDA Communication: Sends join request to all 25 RAIDA servers in parallel using thread pool (~1-2 second execution time).
  5. Response Collection: Collects responses from all RAIDAs and builds POWN string (p=pass, f=fail, e=error, n=no reply).
  6. Authenticity Check: Requires 13+ RAIDAs to respond with 'pass' for successful join.
  7. Coin Creation: If successful, downloads joined coin with new ANs from RAIDA and saves to Bank folder.
  8. Source Coin Cleanup: Removes source coins from Bank folder (they are now destroyed on RAIDA).
  9. Transaction Receipt: Generates receipt file in Receipts folder with join details.

Atomic Operation Guarantee

The join operation is atomic across all 25 RAIDA servers:

  • If 13+ RAIDAs succeed → All source coins destroyed, joined coin created
  • If <13 RAIDAs succeed → Operation fails, source coins remain unchanged
  • No partial state: Either complete success or complete rollback

Coin Selection Strategy

The system will automatically select the best combination of source coins:

Target: 1000 (denomination code 5)

Option 1 (Preferred - Exact Match):
  4× 250 = 1000 ✓ (Minimal coins)

Option 2:
  10× 100 = 1000 ✓ (More coins, less optimal)

Option 3:
  2× 250 + 5× 100 = 1000 ✓ (Mixed denominations)

Option 4:
  1× 250 + 3× 100 + 15× 25 + 5× 5 = 1000 ✓ (Many coins, least optimal)

Selection Priority:
  1. Fewest source coins
  2. Largest denominations first
  3. Avoid leaving odd remainders

Performance Characteristics

Operation Time (Sequential) Time (Multi-threaded)
Serial Number Request ~25 seconds ~1-2 seconds
Join Request (0x095d) ~25 seconds ~1-2 seconds
Download New ANs ~25 seconds ~1-2 seconds
Total ~75 seconds ~3-6 seconds

Examples

JavaScript (fetch)

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

async function joinCoins(targetDenomination) {
    try {
        const response = await fetch(
            `${API_HOST}/api/coins/join?denomination=${targetDenomination}`
        );
        const result = await response.json();

        if (result.status === 'success') {
            console.log('Join Operation (Parameter Validation):');
            console.log(`Target Denomination: ${result.target_denomination}`);
            console.log(`Target Value: ${result.target_value}`);
            console.log(`Wallet: ${result.wallet}`);
            console.log(`Note: ${result.note}`);

            // Future implementation will include:
            // console.log(`Source Coins: ${result.source_count}`);
            // console.log(`Joined Coin SN: ${result.joined_coin.serial_number}`);
            // console.log(`POWN String: ${result.pown_string}`);
            // console.log(`Status: ${result.authenticity_status}`);
        } else {
            console.error('Join failed:', result.message);
        }
    } catch (error) {
        console.error('Error joining coins:', error);
    }
}

// Join 4× 250 coins into 1× 1000 coin (denomination code 5)
joinCoins(5);

// Join 4× 25 coins into 1× 100 coin (denomination code 3)
joinCoins(3);

// Denomination reference map
const DENOMINATIONS = {
    0: 1,
    1: 5,
    2: 25,
    3: 100,
    4: 250,
    5: 1000,
    6: 5000,
    7: 25000,
    8: 100000,
    9: 250000,
    10: 1000000
};

function getDenominationCode(value) {
    return Object.keys(DENOMINATIONS).find(
        key => DENOMINATIONS[key] === value
    );
}

// Example: Join into 1000 coin
const code = getDenominationCode(1000); // Returns "5"
joinCoins(parseInt(code));

cURL

# Join coins into 1000 denomination (code 5)
curl -X GET "http://localhost:8080/api/coins/join?denomination=5"

# Join coins into 100 denomination (code 3)
curl -X GET "http://localhost:8080/api/coins/join?denomination=3"

# Join coins into 25000 denomination (code 7)
curl -X GET "http://localhost:8080/api/coins/join?denomination=7"

# Pretty-print JSON response with jq
curl -X GET "http://localhost:8080/api/coins/join?denomination=5" | jq .

# Check for errors
curl -X GET "http://localhost:8080/api/coins/join?denomination=99" | jq .

Go

package main

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

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

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

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

type RaidaResults struct {
    TotalRaidas  int `json:"total_raidas"`
    SuccessCount int `json:"success_count"`
    FailCount    int `json:"fail_count"`
    ErrorCount   int `json:"error_count"`
}

type JoinResponse struct {
    Status               string       `json:"status"`
    Operation            string       `json:"operation"`
    Message              string       `json:"message"`
    Wallet               string       `json:"wallet"`
    TargetDenomination   int          `json:"target_denomination"`
    TargetValue          int          `json:"target_value"`
    Note                 string       `json:"note"`
    // Future implementation fields:
    SourceCoins          []SourceCoin `json:"source_coins,omitempty"`
    SourceCount          int          `json:"source_count,omitempty"`
    JoinedCoin           *JoinedCoin  `json:"joined_coin,omitempty"`
    RaidaResults         *RaidaResults `json:"raida_results,omitempty"`
    PownString           string       `json:"pown_string,omitempty"`
    AuthenticityStatus   string       `json:"authenticity_status,omitempty"`
}

// Denomination value reference
var DenominationValues = map[int]int{
    0: 1,
    1: 5,
    2: 25,
    3: 100,
    4: 250,
    5: 1000,
    6: 5000,
    7: 25000,
    8: 100000,
    9: 250000,
    10: 1000000,
}

func joinCoins(targetDenomination int) (*JoinResponse, error) {
    url := fmt.Sprintf("%s/api/coins/join?denomination=%d",
        ApiHost, targetDenomination)

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

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

    var result JoinResponse
    if err := json.Unmarshal(body, &result); err != nil {
        return nil, fmt.Errorf("failed to parse JSON: %v", err)
    }

    return &result, nil
}

func main() {
    // Join into 1000 denomination (code 5)
    result, err := joinCoins(5)
    if err != nil {
        panic(err)
    }

    if result.Status == "success" {
        fmt.Println("Join Operation (Parameter Validation):")
        fmt.Printf("Target Denomination: %d\n", result.TargetDenomination)
        fmt.Printf("Target Value: %d\n", result.TargetValue)
        fmt.Printf("Wallet: %s\n", result.Wallet)
        fmt.Printf("Note: %s\n", result.Note)

        // Future implementation will include:
        // fmt.Printf("Source Coins: %d\n", result.SourceCount)
        // fmt.Printf("Joined Coin SN: %d\n", result.JoinedCoin.SerialNumber)
        // fmt.Printf("POWN String: %s\n", result.PownString)
        // fmt.Printf("Status: %s\n", result.AuthenticityStatus)
    } else {
        fmt.Printf("Join failed: %s\n", result.Message)
    }

    // Example: Join different denominations
    denominations := []int{3, 5, 7} // 100, 1000, 25000
    for _, denom := range denominations {
        result, err := joinCoins(denom)
        if err != nil {
            fmt.Printf("Error for denomination %d: %v\n", denom, err)
            continue
        }
        value := DenominationValues[denom]
        fmt.Printf("Validated join into %d (value: %d)\n", denom, value)
    }
}

Join vs Break Operations

Understanding the difference between joining and breaking coins:

Aspect Join (0x095d) Break (0x095c)
Operation Many coins → One coin One coin → Many coins
Direction Smaller → Larger denomination Larger → Smaller denominations
Use Case Consolidate wallet, reduce coin count Make change, prepare exact payments
Example 4× 25 → 1× 100 1× 100 → 4× 25
Source Coins Multiple (2-100+ coins) Single coin
Result Coins Single coin Multiple coins
Value Conservation Sum of sources = Joined coin Source coin = Sum of result coins
RAIDA Protocol Command 0x095d Command 0x095c
💡 When to Use Each Operation

Use Join when you want to:

  • Reduce the total number of coins in your wallet
  • Consolidate many small denominations into fewer large ones
  • Simplify wallet management
  • Prepare for large transactions

Use Break when you want to:

  • Create exact change for payments
  • Split a large coin into multiple smaller denominations
  • Prepare coins for distribution to multiple recipients
  • Increase denomination flexibility

Related Endpoints

/api/coins/break

Break a single large denomination coin into multiple smaller denomination coins. Opposite of join operation.

/api/wallet/balance

Check current wallet balance and available denominations before joining coins.

/api/wallet/show-coins

View detailed list of all coins in Bank folder, including denominations available for joining.

/api/coins/detect

Authenticate coins with RAIDA before joining to ensure all source coins are valid.

Implementation Roadmap

🚧 Development Status

Current Implementation (Parameter Validation Only):

  • ✅ Parameter parsing and validation
  • ✅ Denomination code verification (0-10)
  • ✅ Target value calculation
  • ✅ Active wallet check
  • ✅ Error response formatting

Pending Implementation:

  • ❌ Integration with cmd_join_coins() in C backend
  • ❌ Coin selection algorithm (optimal combination finder)
  • ❌ RAIDA serial number request (find available SN)
  • ❌ RAIDA Join (0x095d) protocol execution
  • ❌ Multi-threaded RAIDA communication (25 parallel requests)
  • ❌ POWN string collection and authenticity check (13+ passes required)
  • ❌ New coin download with ANs from RAIDA
  • ❌ Source coin cleanup (remove from Bank folder)
  • ❌ Joined coin save to Bank folder
  • ❌ Transaction receipt generation

Dependencies:

  • raida_thread_pool_t - Multi-threaded RAIDA execution
  • wallet.h - Coin file I/O and Bank folder management
  • transactions.h - Receipt generation
  • raida.h - RAIDA protocol implementation (0x095d)