Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Musk

SDK for compiling, deploying, and spending Simplicity programs on Elements/Liquid networks.

Overview

Musk provides a high-level Rust interface for working with Simplicity programs. It wraps the SimplicityHL compiler and provides utilities for:

  • Program compilation and instantiation
  • Taproot address generation
  • Transaction construction and signing
  • Witness value management
  • Node connectivity via RPC

Installation

Add musk to your Cargo.toml:

[dependencies]
musk = { path = "../musk" }

Usage

Connecting to Nodes

Musk provides an RpcClient for connecting to Elements/Liquid nodes:

use musk::{NodeConfig, RpcClient};

// Method 1: Load from config file
let client = RpcClient::from_config_file("musk.toml")?;

// Method 2: Create programmatically
let config = NodeConfig::regtest()
    .with_rpc("http://localhost:18884", "user", "password");
let client = RpcClient::new(config)?;

// Method 3: Quick URL-based setup
let client = RpcClient::from_url("http://localhost:18884", "user", "pass")?;

// Test connection
client.test_connection()?;

Configuration File (musk.toml)

[network]
network = "regtest"  # or "testnet", "liquidv1"

[rpc]
url = "http://127.0.0.1:18884"
user = "user"
password = "password"

[chain]
genesis_hash = "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"

Basic Program Example

use musk::{Program, Arguments, RpcClient, NodeConfig};
use musk::client::NodeClient;

// Connect to node
let client = RpcClient::new(NodeConfig::regtest())?;

// Load and compile a program
let program = Program::from_file("my_program.simf")?;
let compiled = program.instantiate(Arguments::default())?;

// Generate an address (uses network-appropriate params)
let address = compiled.address(client.address_params());
println!("Program address: {}", address);

// Fund the program
let txid = client.send_to_address(&address, 100_000_000)?; // 1 BTC
client.generate_blocks(1)?; // Confirm (regtest only)

Building Spending Transactions

use musk::{SpendBuilder, WitnessValues};

// Build a spending transaction
let mut builder = SpendBuilder::new(compiled, utxo)
    .genesis_hash(client.genesis_hash()?);

// Add outputs
builder.add_output_simple(destination, amount, asset);
builder.add_fee(3000, asset);

// Compute sighash for signature generation
let sighash = builder.sighash_all()?;

// Create witness values (with signatures)
let witness = WitnessValues::default();

// Finalize and broadcast
let tx = builder.finalize(witness)?;
let txid = client.broadcast(&tx)?;

With Arguments and Witnesses

use musk::{Program, Arguments, Value, WitnessName};
use std::collections::HashMap;

// Load program with parameters
let program = Program::from_file("p2pk.simf")?;

// Provide arguments
let mut args = HashMap::new();
args.insert(
    WitnessName::from_str_unchecked("ALICE_PUBLIC_KEY"),
    Value::u256(pubkey),
);
let compiled = program.instantiate(Arguments::from(args))?;

// Create witness with signature
let mut witness = HashMap::new();
witness.insert(
    WitnessName::from_str_unchecked("ALICE_SIGNATURE"),
    Value::byte_array(signature),
);

let tx = builder.finalize(WitnessValues::from(witness))?;

Architecture

Musk is designed to be network-agnostic through the NodeClient trait:

┌─────────────────────────────────────────────────────────────┐
│                         Your App                            │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                          Musk                               │
│  ┌─────────────┐  ┌────────────────────┐  ┌──────────────┐  │
│  │   Program   │→ │InstantiatedProgram │→ │ SpendBuilder │  │
│  │   (.simf)   │  │     (Address)      │  │ (Transaction)│  │
│  └─────────────┘  └────────────────────┘  └──────────────┘  │
│                                              │              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │               NodeClient trait                       │   │
│  │  ┌───────────┐  ┌───────────┐  ┌───────────────┐    │   │
│  │  │ RpcClient │  │  (spray)  │  │ (your impl)   │    │   │
│  │  │ (built-in)│  │           │  │               │    │   │
│  │  └───────────┘  └───────────┘  └───────────────┘    │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              Elements/Liquid Node (RPC)                     │
│         regtest  │  testnet  │  liquidv1 (mainnet)          │
└─────────────────────────────────────────────────────────────┘

Features

  • serde: Enable serialization support (default)
  • rpc: Enable RpcClient and config file support (default)

To use without RPC support:

[dependencies]
musk = { path = "../musk", default-features = false, features = ["serde"] }

Network Support

Network Default Port Address Params
regtest 18884 ELEMENTS
testnet 18892 LIQUID_TESTNET
liquidv1 7041 LIQUID

Examples

See the examples/ directory:

  • basic_usage.rs - Simple program workflow
  • rpc_client.rs - Connecting to nodes with RpcClient

Run examples:

cargo run --example basic_usage
cargo run --example rpc_client

License

MIT OR Apache-2.0

About

SDK for compiling, deploying, and spending Simplicity contracts in Rust applications.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages