The board (“boarding”) module enables users to onboard on-chain UTXOs into Ark by creating VTXOs backed by a co-signed exit transaction. This is the primary mechanism for bringing Bitcoin into the Ark.
Overview
Boarding allows users to:
Convert on-chain UTXOs into off-chain VTXOs
Join the Ark without waiting for a round
Create spendable VTXOs with cooperative server signatures
Exit anytime using the co-signed transaction
The process uses MuSig2 for compact co-signing between user and server.
BoardBuilder
A state-machine builder for creating board VTXOs.
pub struct BoardBuilder < S : BuilderState > {
pub user_pubkey : PublicKey ,
pub expiry_height : BlockHeight ,
pub server_pubkey : PublicKey ,
pub exit_delta : BlockDelta ,
amount : Option < Amount >,
fee : Option < Amount >,
utxo : Option < OutPoint >,
user_pub_nonce : Option < PublicNonce >,
user_sec_nonce : Option < SecretNonce >,
exit_data : Option < ExitData >,
}
Builder States
pub mod state {
pub struct Preparing ; // Initial state
pub struct CanGenerateNonces ; // Funding details set
pub struct ServerCanCosign ; // Server can cosign
pub struct CanFinish ; // User can finalize
}
State Transitions
User Path:
Preparing → set_funding_details() → CanGenerateNonces → generate_user_nonces() → CanFinish → build_vtxo()
Server Path:
(skip Preparing) → new_for_cosign() → ServerCanCosign → server_cosign()
Board Flow
User creates builder : BoardBuilder::new()
User creates funding tx : Pays to funding_script_pubkey()
User sets funding details : set_funding_details()
User generates nonces : generate_user_nonces()
User sends board info to server
Server creates builder : BoardBuilder::new_for_cosign()
Server cosigns : server_cosign() → sends response to user
User validates response : verify_cosign_response()
User builds VTXO : build_vtxo()
Construction (Preparing State)
Creating a Builder
pub fn new (
user_pubkey : PublicKey ,
expiry_height : BlockHeight ,
server_pubkey : PublicKey ,
exit_delta : BlockDelta ,
) -> BoardBuilder < state :: Preparing >
Create a new board builder. Sets the basic parameters for the VTXO.
user_pubkey: User’s public key for the VTXO
expiry_height: Block height when VTXO expires
server_pubkey: Server’s public key for co-signing
exit_delta: Relative timelock for unilateral exits (in blocks)
Funding Script
pub fn funding_script_pubkey ( & self ) -> ScriptBuf
Get the scriptPubkey to send board funds to. This is a MuSig2 aggregated key with an expiry clause.
The funding script is:
Keyspend : MuSig2(user_pubkey, server_pubkey) - co-signed exit
Script path : Server can sweep after expiry_height
Setting Funding Details (CanGenerateNonces State)
pub fn set_funding_details (
self ,
amount : Amount ,
fee : Amount ,
utxo : OutPoint ,
) -> Result < BoardBuilder < state :: CanGenerateNonces >, BoardFundingError >
Set the UTXO, amount, and fee. Computes exit transaction and transitions to CanGenerateNonces.
amount: Total amount in the funding UTXO
fee: Fee to deduct for the exit transaction
utxo: The outpoint of the funding transaction output
Validation:
Amount must be > 0
Fee must be ≤ amount
Amount after fee must be > 0
Funding Errors
pub enum BoardFundingError {
FeeHigherThanAmount { amount : Amount , fee : Amount },
ZeroAmount ,
ZeroAmountAfterFee { amount : Amount , fee : Amount },
}
Generating Nonces (CanFinish State)
pub fn generate_user_nonces ( self ) -> BoardBuilder < state :: CanFinish >
Generate MuSig2 nonces for signing the exit transaction. Transitions to CanFinish state.
// Available in CanSign trait (ServerCanCosign + CanFinish)
pub fn user_pub_nonce ( & self ) -> & PublicNonce
Access the generated public nonce (needed for server cosigning).
Reconstructing from VTXO (CanGenerateNonces State)
pub fn new_from_vtxo (
vtxo : & Vtxo ,
funding_tx : & Transaction ,
server_pubkey : PublicKey ,
) -> Result < Self , BoardFromVtxoError >
Reconstruct a board builder from an existing board VTXO. Used to validate that a VTXO is a legitimate board from the server.
Validation : Verify a VTXO is a genuine board from the server
Tracking : Server can reconstruct board state for monitoring
Recovery : Recreate builder state from stored VTXO
Caller must call vtxo.validate() before using this constructor.
Board VTXO Errors
pub enum BoardFromVtxoError {
FundingTxMismatch { expected : Txid , got : Txid },
ServerPubkeyMismatch { expected : PublicKey , got : PublicKey },
VtxoIdMismatch { expected : OutPoint , got : OutPoint },
IncorrectGenesisItemCount { genesis_count : usize },
}
Exit Transaction (CanGenerateNonces State)
pub fn exit_tx ( & self ) -> & Transaction
Get the unsigned exit transaction. This spends the funding UTXO and creates the VTXO output.
pub fn exit_txid ( & self ) -> Txid
Get the txid of the exit transaction.
Building Internal VTXOs (CanGenerateNonces State)
pub fn build_internal_unsigned_vtxos ( & self ) -> Vec < ServerVtxo >
Build the internal unsigned VTXOs for server tracking.
Returns two VTXOs :
Expiry VTXO (empty genesis): Used by server to track the funding UTXO for sweeping on expiry
Pubkey VTXO (arkoor genesis): The actual board VTXO with an arkoor genesis transition
Both share the same outpoint (the funding UTXO) but have different policies.
Spend Info (CanGenerateNonces State)
pub fn spend_info ( & self ) -> Vec <( VtxoId , Txid )>
Returns the VTXO ID and spending transaction ID. For boards, this is:
(funding_utxo, exit_txid)
Server Cosigning (ServerCanCosign State)
Creating Server Builder
pub fn new_for_cosign (
user_pubkey : PublicKey ,
expiry_height : BlockHeight ,
server_pubkey : PublicKey ,
exit_delta : BlockDelta ,
amount : Amount ,
fee : Amount ,
utxo : OutPoint ,
user_pub_nonce : PublicNonce ,
) -> BoardBuilder < state :: ServerCanCosign >
Server constructor using information from the user. Computes exit transaction with user’s nonce.
Cosigning
pub fn server_cosign ( & self , key : & Keypair ) -> BoardCosignResponse
Server cosigns the exit transaction. Returns nonce and partial signature for the user.
BoardCosignResponse
pub struct BoardCosignResponse {
pub pub_nonce : PublicNonce ,
pub partial_signature : PartialSignature ,
}
Server’s response containing the MuSig2 nonce and partial signature.
User Finalization (CanFinish State)
Validating Server Response
pub fn verify_cosign_response ( & self , server_cosign : & BoardCosignResponse ) -> bool
Validate the server’s partial signature. Returns true if valid.
Always verify before calling build_vtxo()!
Building the VTXO
pub fn build_vtxo (
self ,
server_cosign : & BoardCosignResponse ,
user_key : & Keypair ,
) -> Result < Vtxo , IncorrectSigningKeyError >
Finalize the board and create the VTXO. Combines user and server partial signatures.
Verify user_key matches user_pubkey
Aggregate nonces
Compute user’s partial signature
Combine with server’s partial signature
Verify final signature
Build and return VTXO
The resulting VTXO:
Amount: amount - fee
Policy: VtxoPolicy::Pubkey(user_pubkey)
Genesis: Single Cosigned transition with final signature
Point: exit_tx output 0
Constants
pub const BOARD_FUNDING_TX_VTXO_VOUT : u32 = 0 ;
The output index of the board VTXO in the exit transaction (always 0).
Usage Examples
User: Creating a Board VTXO
let user_key = Keypair :: new ( SECP256K1_CONTEXT , & mut rng );
let server_pubkey : PublicKey = // ... from server
let expiry_height = 850_000 ;
let exit_delta = 2016 ; // ~2 weeks
// 1. Create builder
let builder = BoardBuilder :: new (
user_key . public_key (),
expiry_height ,
server_pubkey ,
exit_delta ,
);
// 2. Get funding script
let funding_spk = builder . funding_script_pubkey ();
// 3. Create funding transaction
let funding_tx = Transaction {
// ... transaction paying to funding_spk
};
let funding_amount = Amount :: from_sat ( 100_000 );
let fee = Amount :: from_sat ( 500 );
let utxo = OutPoint :: new ( funding_tx . compute_txid (), 0 );
// 4. Set funding details
let builder = builder . set_funding_details (
funding_amount ,
fee ,
utxo ,
) ? ;
// 5. Generate nonces
let builder = builder . generate_user_nonces ();
// 6. Send to server:
// - user_pubkey
// - expiry_height
// - exit_delta
// - amount, fee, utxo
// - user_pub_nonce
// ... receive cosign_response from server ...
// 7. Verify and build
if ! builder . verify_cosign_response ( & cosign_response ) {
return Err ( "Invalid server signature" );
}
let vtxo = builder . build_vtxo ( & cosign_response , & user_key ) ? ;
// 8. Validate the VTXO
vtxo . validate ( & funding_tx ) ? ;
Server: Cosigning a Board Request
let server_key : Keypair = // ...
// Receive from user:
let user_pubkey : PublicKey = // ...
let expiry_height : BlockHeight = // ...
let exit_delta : BlockDelta = // ...
let amount : Amount = // ...
let fee : Amount = // ...
let utxo : OutPoint = // ...
let user_pub_nonce : PublicNonce = // ...
// 1. Create server builder
let builder = BoardBuilder :: new_for_cosign (
user_pubkey ,
expiry_height ,
server_key . public_key (),
exit_delta ,
amount ,
fee ,
utxo ,
user_pub_nonce ,
);
// 2. Cosign
let response = builder . server_cosign ( & server_key );
// 3. Store internal VTXOs for tracking
let internal_vtxos = builder . build_internal_unsigned_vtxos ();
let spend_info = builder . spend_info ();
// Store in database:
// - internal_vtxos (for expiry tracking and arkoor origin)
// - spend_info (VTXO spending relationships)
// 4. Send response to user
// response.pub_nonce
// response.partial_signature
Validating an Existing Board VTXO
let vtxo : Vtxo = // ... received board VTXO
let funding_tx : Transaction = // ... from chain
let server_pubkey : PublicKey = // ...
// First validate the VTXO
vtxo . validate ( & funding_tx ) ? ;
// Reconstruct builder to verify it's a legitimate board
let builder = BoardBuilder :: new_from_vtxo (
& vtxo ,
& funding_tx ,
server_pubkey ,
) ? ;
// If successful, this is a valid board VTXO from this server
let internal_vtxos = builder . build_internal_unsigned_vtxos ();
// internal_vtxos[0] = Expiry VTXO (for sweep tracking)
// internal_vtxos[1] = Pubkey VTXO (arkoor-compatible)
Transaction Structure
Funding UTXO (on-chain)
↓
[Exit Transaction] ← Co-signed by user + server
↓
VTXO
Funding UTXO Policy:
Keyspend: MuSig2(user, server) - requires both signatures
Script path: Server after expiry_height
Exit Transaction:
Input: Funding UTXO (keyspend path)
Output 0: VTXO output (user’s final policy)
Output 1: Fee anchor (P2A)
Resulting VTXO:
Genesis: Single Cosigned transition
Policy: Pubkey(user_pubkey) (can be spent in rounds or arkoor)
Amount: funding_amount - fee
Important Notes
Always validate the server’s cosign response with verify_cosign_response() before calling build_vtxo(). Invalid signatures will cause the VTXO to be unusable.
Board VTXOs have a single genesis item with a Cosigned transition. The signature is the final MuSig2 aggregate signature over the exit transaction.
The board funding UTXO can be swept by the server after expiry_height if not spent. This ensures servers can reclaim stuck funds.
Use build_internal_unsigned_vtxos() to create tracking VTXOs:
Expiry VTXO tracks the funding UTXO for server sweeping
Arkoor VTXO enables the board to be spent out-of-round