Skip to main content

What are Rounds?

Rounds are the fundamental coordination mechanism in Ark. Every round_interval (configured in ArkInfo), the server creates a new round transaction that:
  • Refreshes existing VTXOs to extend their lifetime
  • Processes off-chain payments between users
  • Settles Lightning payments into VTXOs
  • Handles cooperative offboards to on-chain addresses
  • Consolidates and optimizes VTXO distribution
Rounds are the “heartbeat” of Ark - they keep the system alive by continuously refreshing VTXOs before they expire and settling pending operations.

Round Lifecycle

The round system is defined in lib/src/rounds.rs. Each round progresses through several stages:

Round Identification

Round Sequence Number
pub struct RoundSeq(u64);  // Simple incrementing counter
  • Identifies rounds before they’re finalized
  • Increments with each new round attempt
  • Used during round construction
Round ID
pub struct RoundId(Txid);  // Transaction ID of round tx
  • Permanent identifier after round completion
  • Based on the round transaction’s txid
  • Used to reference historical rounds

Round Events

Clients receive events as rounds progress:
pub enum RoundEvent {
    Attempt(RoundAttempt),
    VtxoProposal(VtxoProposal),
    Finished(RoundFinished),
}

1. Round Attempt

pub struct RoundAttempt {
    pub round_seq: RoundSeq,
    pub attempt_seq: usize,
    pub challenge: RoundAttemptChallenge,
}
  • Server announces a new round attempt
  • Includes a random challenge for ownership proofs
  • Clients prepare to participate
Challenge Mechanism: Prevents replay attacks by requiring clients to sign a unique challenge for each round attempt.

2. VTXO Proposal

pub struct VtxoProposal {
    pub round_seq: RoundSeq,
    pub attempt_seq: usize,
    pub unsigned_round_tx: Transaction,
    pub vtxos_spec: VtxoTreeSpec,
    pub cosign_agg_nonces: Vec<AggregatedNonce>,
}
  • Server proposes the round transaction structure
  • Includes VTXO tree specification
  • Contains aggregated MuSig2 nonces for signing
  • Clients validate and provide partial signatures

3. Round Finished

pub struct RoundFinished {
    pub round_seq: RoundSeq,
    pub attempt_seq: usize,
    pub cosign_sigs: Vec<Signature>,
    pub signed_round_tx: Transaction,
}
  • Server provides final aggregated signatures
  • Signed round transaction ready for broadcast
  • Clients extract and store their new VTXOs

Round Transaction Structure

A round transaction must have at minimum:
pub const MIN_ROUND_TX_OUTPUTS: usize = 2;
pub const ROUND_TX_VTXO_TREE_VOUT: u32 = 0;  // VTXO tree root

Output Layout

Round Transaction
├─ Output 0: VTXO Tree Root
├─ Output 1: Connector Chain Root
├─ Output 2+: Offboard Outputs (optional)
└─ Output N: Fee Anchor
VTXO Tree Root (Output 0)
  • Root of the radix-4 tree containing all user VTXOs
  • Always at vout 0 by protocol convention
  • Single UTXO shared by all participants
Connector Chain (Output 1)
  • Chain of connector outputs for future rounds
  • Enables efficient forfeit/penalty transactions
  • Funded according to ConnectorChain::required_budget(len)
Offboard Outputs (Output 2+)
  • Direct on-chain payments to user addresses
  • Users exiting funds cooperatively
  • More efficient than unilateral exits
Fee Anchor
  • Allows CPFP fee-bumping if needed
  • Standard P2A (Pay-to-Anchor) output

VTXO Tree Structure

VTXOs in a round are organized in a radix-4 tree (defined in lib/src/tree/mod.rs):
const RADIX: usize = 4;  // Each node has up to 4 children

pub struct Tree {
    nodes: Vec<Node>,
    nb_leaves: usize,
}

Tree Construction

Example: 10 VTXOs organized in radix-4 tree
                    Root (Node 13)
                       |
        ┌──────────────┼──────────────┐
        │              │              │
    Node 10        Node 11        Node 12
     ├─┬─┬─┐      ├─┬─┬─┐        ├───┐
     │ │ │ │      │ │ │ │        │   │
    V0 V1V2V3     V4 V5V6V7      V8  V9
  (Leaves 0-3)  (Leaves 4-7)  (Leaves 8-9)
Tree Properties:
  • Leaves are user VTXOs
  • Internal nodes are intermediate outputs
  • Each level can have up to 4 children
  • Minimizes transaction chain depth
  • Calculated size: Tree::nb_nodes_for_leaves(nb_leaves)

Node Structure

pub struct Node {
    idx: u32,                     // Node index
    parent: Option<u32>,          // Parent node
    children: [Option<u32>; 4],   // Up to 4 children
    leaves: (u32, u32),           // Leaf range (exclusive)
    level: u32,                   // Distance from leaves
}
Levels:
  • Level 0: Leaf nodes (user VTXOs)
  • Level 1: Internal nodes with leaf children
  • Level N: Closer to root
  • Root: Highest level

Round Participation

Inputs to a Round

Users can provide inputs to participate:
pub struct VtxoIdInput {
    pub vtxo_id: VtxoId,
    pub ownership_proof: Signature,  // Over challenge + vtxo_id
}
Ownership Proof: Schnorr signature proving control of the VTXO’s private key, signed over:
message = CHALLENGE_PREFIX || round_challenge || vtxo_id
This prevents:
  • Replay attacks across different rounds
  • Unauthorized spending of VTXOs
  • Round manipulation by non-owners

Outputs from a Round

Users request new VTXOs:
pub struct VtxoRequest {
    pub amount: Amount,
    pub policy: VtxoPolicy,
}

pub struct SignedVtxoRequest {
    pub vtxo: VtxoRequest,
    pub cosign_pubkey: PublicKey,     // Ephemeral key for this round
    pub nonces: Vec<PublicNonce>,     // MuSig2 nonces
}
Cosign Pubkey: One-time key used only for this round:
  • Derived deterministically from user’s master key
  • Used to cosign the VTXO tree transactions
  • Should be forgotten after signing (for privacy)
Nonce Generation: MuSig2 requires nb_round_nonces nonces (from ArkInfo):
let nb_nonces = ark_info.nb_round_nonces;  // Typically matches tree depth
let nonces: Vec<PublicNonce> = (0..nb_nonces)
    .map(|_| generate_nonce())
    .collect();

Round Timing

Interval Configuration

pub round_interval: Duration  // From ArkInfo
Typical values:
  • Mainnet: 5-15 minutes (balances UX vs efficiency)
  • Signet/Testnet: 1-5 minutes (faster testing)
  • Custom: Configurable by service provider

Round Schedule

Rounds occur on a regular schedule:
Round 1:  T + 0:00
Round 2:  T + round_interval
Round 3:  T + 2×round_interval
...
If a round fails (not enough participants, signing issues, etc.), a new attempt starts immediately with incremented attempt_seq.

Round Fees

Fees are charged according to FeeSchedule in ArkInfo.fees: Typical fee components:
  1. Base round fee: Fixed cost per VTXO
  2. Consolidation fee: Charged when combining VTXOs
  3. Tree position fee: May vary by tree depth
  4. Offboard fee: Covers on-chain transaction costs
Fee Calculation Example:
Input VTXOs:  3 × 10,000 sats = 30,000 sats
Output VTXO:  1 × 28,500 sats
Round Fee:    1,500 sats

Round Transaction Signing

Rounds use MuSig2 for efficient multi-signature aggregation:

Signing Protocol

  1. Nonce Exchange
    • Each participant generates public nonces
    • Server aggregates: agg_nonce = musig::nonce_agg([user_nonces, server_nonces])
  2. Partial Signatures
    • Each tree transaction signed independently
    • Users provide partial signatures for their inputs
    • Server provides partial signatures for all outputs
  3. Signature Aggregation
    • Server combines partial signatures
    • Produces final Schnorr signature for each transaction
    • Broadcasts round transaction with aggregated signatures
let agg_nonce = musig::nonce_agg(&[&user_nonce, &server_nonce]);
let agg_pk = musig::tweaked_key_agg([user_pk, server_pk], tap_tweak);
let session = musig::Session::new(&agg_pk, agg_nonce, &sighash);
let partial_sig = session.partial_sign(&user_keypair, &user_nonce);

Round Failures and Retries

Rounds can fail for various reasons: Common failure modes:
  • Insufficient participants
  • Signing timeouts
  • Invalid partial signatures
  • Transaction validation failures
  • Network issues
Retry mechanism:
attempt_seq: usize  // Increments with each retry
  • Same round_seq, incremented attempt_seq
  • New challenge generated for security
  • Participants must re-submit inputs and signatures
Clients should implement timeout logic - don’t wait indefinitely for a round that may never complete.

Connector Chains

Connector chains (defined in lib/src/connectors.rs) enable efficient forfeit transactions:
pub struct ConnectorChain {
    len: usize,              // Number of connectors
    spk: ScriptBuf,          // P2TR scriptPubKey
    utxo: OutPoint,          // Starting point in round tx
}

Purpose

  • Forfeit transactions: Penalize malicious behavior
  • Efficiency: Pre-funded connectors avoid complex fee markets
  • Flexibility: Different lengths for different security models

Budget Calculation

pub fn required_budget(len: usize) -> Amount {
    P2TR_DUST * len as u64  // 330 sats × length
}
Connector chain weights:
pub fn total_weight(len: usize) -> Weight {
    (len - 1) as u64 * TX_WEIGHT  // 167 vB per connector tx
}

Monitoring Rounds

Client Responsibilities

Essential round monitoring:
  1. Listen for round events on the server’s event stream
  2. Validate proposals before signing
  3. Submit partial signatures promptly
  4. Extract and store new VTXOs from finished rounds
  5. Verify round transaction confirms on-chain
  6. Track VTXO expiries and refresh before expiry

Round Metrics

Key metrics to monitor:
  • Round completion rate
  • Average round interval
  • Participation count
  • Fee trends
  • Retry frequency

Best Practices

DO:
  • Participate in rounds regularly to refresh VTXOs
  • Validate all VTXO proposals before signing
  • Monitor round events continuously
  • Keep ephemeral signing keys secure during rounds
  • Verify round transactions confirm on-chain
DON’T:
  • Reuse cosign pubkeys across rounds
  • Skip ownership proof validation
  • Sign without validating the VTXO tree structure
  • Assume rounds always succeed
  • Ignore failed round attempts

Round Transaction Example

A complete round transaction structure:
Round Transaction (txid: abc123...)
Inputs:
  [0] Connector from previous round
  [1] Forfeit anchor (if needed)
  
Outputs:
  [0] VTXO Tree Root (P2TR, 50,000 sats)
      → Contains 10 user VTXOs in radix-4 tree
  [1] Connector Chain (P2TR, 3,300 sats)
      → 10 connectors for future rounds
  [2] Offboard to bc1q... (P2WPKH, 95,000 sats)
      → User cooperative exit
  [3] Fee Anchor (P2A, 330 sats)
      → CPFP capability

Further Reading

VTXOs

Understanding VTXO structure

Exits

What happens when rounds fail

Build docs developers (and LLMs) love