use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::layer2::{
AssetParams, AssetTransfer, Layer2Error, Layer2Protocol, Proof, ProtocolState,
TransactionStatus, TransferResult, ValidationResult, VerificationResult,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChannelState {
Creating,
Open,
Closing,
Closed,
Disputed,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CommitmentType {
MultiSig2of2,
MuSig2of2,
TaprootKeySpend,
TaprootScriptSpend,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateChannelConfig {
pub network: String,
pub capacity: u64,
pub time_lock: u32,
pub commitment_type: CommitmentType,
pub use_taproot: bool,
pub fee_rate: u64,
}
impl Default for StateChannelConfig {
fn default() -> Self {
Self {
network: "mainnet".to_string(),
capacity: 1_000_000, time_lock: 144, commitment_type: CommitmentType::TaprootKeySpend,
use_taproot: true,
fee_rate: 10, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateUpdate {
pub channel_id: String,
pub version: u64,
pub balance_a: u64,
pub balance_b: u64,
pub timestamp: u64,
pub signatures: Vec<String>,
}
#[derive(Debug)]
pub struct StateChannel {
pub channel_id: String,
pub config: StateChannelConfig,
pub state: ChannelState,
pub balance_a: u64,
pub balance_b: u64,
pub pubkey_a: String,
pub pubkey_b: String,
pub version: u64,
pub updates: Vec<StateUpdate>,
pub transactions: HashMap<String, Vec<u8>>,
}
impl StateChannel {
pub fn new(
config: StateChannelConfig,
pubkey_a: &str,
pubkey_b: &str,
initial_balance_a: u64,
initial_balance_b: u64,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if initial_balance_a + initial_balance_b != config.capacity {
return Err(Box::new(Layer2Error::Protocol(format!(
"Balances must sum to capacity: {} != {}",
initial_balance_a + initial_balance_b,
config.capacity
))));
}
let channel_id = format!(
"sc_{}_{}",
pubkey_a.chars().take(8).collect::<String>(),
pubkey_b.chars().take(8).collect::<String>()
);
let updates = Vec::new();
let transactions = HashMap::new();
Ok(Self {
channel_id,
config,
state: ChannelState::Creating,
balance_a: initial_balance_a,
balance_b: initial_balance_b,
pubkey_a: pubkey_a.to_string(),
pubkey_b: pubkey_b.to_string(),
version: 0,
updates,
transactions,
})
}
pub fn new_default() -> Self {
let config = StateChannelConfig::default();
let pubkey_a = "02d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c";
let pubkey_b = "03a36339f413da869df12b1ab0def91749413a0dee87f0bfa85ba7196e6cdad102";
let half_capacity = config.capacity / 2;
match Self::new(config, pubkey_a, pubkey_b, half_capacity, half_capacity) {
Ok(channel) => channel,
Err(_) => {
Self {
channel_id: "sc_default".to_string(),
config: StateChannelConfig::default(),
state: ChannelState::Creating,
balance_a: 500_000,
balance_b: 500_000,
pubkey_a: pubkey_a.to_string(),
pubkey_b: pubkey_b.to_string(),
version: 0,
updates: Vec::new(),
transactions: HashMap::new(),
}
}
}
}
}
impl Default for StateChannel {
fn default() -> Self {
Self::new_default()
}
}
impl StateChannel {
pub fn open(&mut self) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
if self.state != ChannelState::Creating {
return Err(Box::new(Layer2Error::Protocol(
"Channel must be in Creating state to open".to_string(),
)));
}
let funding_tx_id = format!("funding_{}", self.channel_id);
let tx_data = vec![0u8; 32];
self.transactions.insert(funding_tx_id.clone(), tx_data);
self.state = ChannelState::Open;
Ok(funding_tx_id)
}
pub fn update_state(
&mut self,
balance_a: u64,
balance_b: u64,
signatures: Vec<String>,
) -> Result<StateUpdate, Box<dyn std::error::Error + Send + Sync>> {
if self.state != ChannelState::Open {
return Err(Box::new(Layer2Error::Protocol(
"Channel must be open to update state".to_string(),
)));
}
if balance_a + balance_b != self.config.capacity {
return Err(Box::new(Layer2Error::Protocol(format!(
"Balances must sum to capacity: {} != {}",
balance_a + balance_b,
self.config.capacity
))));
}
if signatures.len() != 2 {
return Err(Box::new(Layer2Error::Protocol(
"Must provide exactly 2 signatures".to_string(),
)));
}
self.version += 1;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let update = StateUpdate {
channel_id: self.channel_id.clone(),
version: self.version,
balance_a,
balance_b,
timestamp,
signatures,
};
self.balance_a = balance_a;
self.balance_b = balance_b;
self.updates.push(update.clone());
Ok(update)
}
pub fn close_cooperative(
&mut self,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
if self.state != ChannelState::Open {
return Err(Box::new(Layer2Error::Protocol(
"Channel must be open to close cooperatively".to_string(),
)));
}
let closing_tx_id = format!("closing_{}", self.channel_id);
let tx_data = vec![0u8; 32];
self.transactions.insert(closing_tx_id.clone(), tx_data);
self.state = ChannelState::Closing;
Ok(closing_tx_id)
}
pub fn force_close(&mut self) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
if self.state != ChannelState::Open && self.state != ChannelState::Disputed {
return Err(Box::new(Layer2Error::Protocol(
"Channel must be open or disputed to force close".to_string(),
)));
}
let force_closing_tx_id = format!("force_closing_{}", self.channel_id);
let tx_data = vec![0u8; 32];
self.transactions
.insert(force_closing_tx_id.clone(), tx_data);
self.state = ChannelState::Closing;
Ok(force_closing_tx_id)
}
pub fn get_latest_update(&self) -> Option<&StateUpdate> {
self.updates.last()
}
pub fn get_update_by_version(&self, version: u64) -> Option<&StateUpdate> {
self.updates.iter().find(|u| u.version == version)
}
pub fn get_transaction(&self, tx_id: &str) -> Option<&Vec<u8>> {
self.transactions.get(tx_id)
}
}
impl crate::layer2::Layer2ProtocolTrait for StateChannel {
fn initialize(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
fn get_state(&self) -> Result<ProtocolState, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::create_protocol_state(
"1.0.0",
2,
Some(self.config.capacity),
self.state == ChannelState::Open,
))
}
fn submit_transaction(
&self,
tx_data: &[u8],
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let tx_id = format!("tx_{}", hex::encode(&tx_data[0..4]));
Ok(tx_id)
}
fn check_transaction_status(
&self,
tx_id: &str,
) -> Result<TransactionStatus, Box<dyn std::error::Error + Send + Sync>> {
if self.transactions.contains_key(tx_id) {
Ok(TransactionStatus::Confirmed)
} else {
Ok(TransactionStatus::Pending)
}
}
fn sync_state(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
fn issue_asset(
&self,
_params: AssetParams,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
Err(Box::new(Layer2Error::Protocol(
"Asset issuance not supported in state channels".to_string(),
)))
}
fn transfer_asset(
&self,
_transfer: AssetTransfer,
) -> Result<TransferResult, Box<dyn std::error::Error + Send + Sync>> {
if self.state != ChannelState::Open {
return Err(Box::new(Layer2Error::Protocol(
"Channel must be open to transfer assets".to_string(),
)));
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
Ok(TransferResult {
tx_id: format!("sc_transfer_{timestamp}"),
status: TransactionStatus::Confirmed,
fee: Some(0), timestamp,
})
}
fn verify_proof(
&self,
proof: Proof,
) -> Result<VerificationResult, Box<dyn std::error::Error + Send + Sync>> {
let is_valid = proof.proof_type == "state_update_proof";
let _timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
Ok(crate::layer2::create_verification_result(
is_valid,
if is_valid {
None
} else {
Some("Invalid proof type".to_string())
},
))
}
fn validate_state(
&self,
_state_data: &[u8],
) -> Result<ValidationResult, Box<dyn std::error::Error + Send + Sync>> {
let _timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
Ok(crate::layer2::create_validation_result(true, vec![]))
}
}
#[derive(Debug)]
pub struct StateChannelsProtocol {
channels: HashMap<String, StateChannel>,
}
impl StateChannelsProtocol {
pub fn new() -> Self {
Self {
channels: HashMap::new(),
}
}
}
impl Default for StateChannelsProtocol {
fn default() -> Self {
Self::new()
}
}
#[async_trait::async_trait]
impl crate::layer2::Layer2Protocol for StateChannelsProtocol {
async fn initialize(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
async fn connect(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
async fn get_state(
&self,
) -> Result<crate::layer2::ProtocolState, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::create_protocol_state(
"1.0.0",
self.channels.len() as u32,
Some(4000000),
true,
))
}
async fn submit_transaction(
&self,
_tx_data: &[u8],
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
Ok("mock_state_channel_tx_id".to_string())
}
async fn check_transaction_status(
&self,
_tx_id: &str,
) -> Result<crate::layer2::TransactionStatus, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::TransactionStatus::Confirmed)
}
async fn sync_state(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
async fn issue_asset(
&self,
_params: crate::layer2::AssetParams,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
Ok("mock_state_channel_asset_id".to_string())
}
async fn transfer_asset(
&self,
_transfer: crate::layer2::AssetTransfer,
) -> Result<crate::layer2::TransferResult, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::TransferResult {
tx_id: "mock_state_channel_transfer_id".to_string(),
status: crate::layer2::TransactionStatus::Confirmed,
fee: Some(100),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
async fn verify_proof(
&self,
_proof: crate::layer2::Proof,
) -> Result<crate::layer2::VerificationResult, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::create_verification_result(true, None))
}
async fn validate_state(
&self,
_state_data: &[u8],
) -> Result<crate::layer2::ValidationResult, Box<dyn std::error::Error + Send + Sync>> {
Ok(crate::layer2::create_validation_result(true, vec![]))
}
}
#[async_trait::async_trait]
impl Layer2Protocol for StateChannel {
async fn initialize(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Asynchronously initializing State Channel...");
Ok(())
}
async fn connect(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Asynchronously connecting State Channel...");
Ok(())
}
async fn get_state(&self) -> Result<ProtocolState, Box<dyn std::error::Error + Send + Sync>> {
println!("Asynchronously getting State Channel state...");
Ok(ProtocolState {
version: "1.0".to_string(),
connections: 1,
capacity: Some(self.config.capacity),
operational: true,
height: 0,
hash: "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
async fn submit_transaction(
&self,
tx_data: &[u8],
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously submitting transaction to State Channel: {} bytes",
tx_data.len()
);
Ok(format!("tx_{}", hex::encode(&tx_data[0..4])))
}
async fn check_transaction_status(
&self,
tx_id: &str,
) -> Result<TransactionStatus, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously checking State Channel transaction status: {}",
tx_id
);
Ok(TransactionStatus::Confirmed)
}
async fn sync_state(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Asynchronously syncing State Channel state...");
Ok(())
}
async fn issue_asset(
&self,
params: AssetParams,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously issuing asset {} on State Channel",
params.name
);
Ok(format!("sc_asset_{}", params.asset_id))
}
async fn transfer_asset(
&self,
transfer: AssetTransfer,
) -> Result<TransferResult, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously transferring {} of asset {} to {} on State Channel",
transfer.amount, transfer.asset_id, transfer.recipient
);
Ok(TransferResult {
tx_id: format!("sc_transfer_{}", transfer.asset_id),
status: TransactionStatus::Confirmed,
fee: Some(100),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
async fn verify_proof(
&self,
proof: Proof,
) -> Result<VerificationResult, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously verifying {} proof on State Channel",
proof.proof_type
);
Ok(VerificationResult {
valid: true,
is_valid: true,
error: None,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
async fn validate_state(
&self,
state_data: &[u8],
) -> Result<ValidationResult, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Asynchronously validating state on State Channel: {} bytes",
state_data.len()
);
Ok(ValidationResult {
is_valid: true,
violations: vec![],
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_channel_creation() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let config = StateChannelConfig {
network: "testnet".to_string(),
capacity: 1_000_000, time_lock: 144, commitment_type: CommitmentType::TaprootKeySpend,
use_taproot: true,
fee_rate: 1, };
let pubkey_a = "0283863a78ec0df67ae8f369e4082a1f67ce09e309e3ce35c6dc4a7e2cb425993c";
let pubkey_b = "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9";
let channel = StateChannel::new(config, pubkey_a, pubkey_b, 600_000, 400_000)?;
assert_eq!(channel.state, ChannelState::Creating);
assert_eq!(channel.balance_a, 600_000);
assert_eq!(channel.balance_b, 400_000);
assert_eq!(channel.version, 0);
assert!(channel.updates.is_empty());
Ok(())
}
#[test]
fn test_state_channel_open_and_update() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let config = StateChannelConfig {
network: "testnet".to_string(),
capacity: 1_000_000, time_lock: 144, commitment_type: CommitmentType::TaprootKeySpend,
use_taproot: true,
fee_rate: 1, };
let pubkey_a = "0283863a78ec0df67ae8f369e4082a1f67ce09e309e3ce35c6dc4a7e2cb425993c";
let pubkey_b = "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9";
let mut channel = StateChannel::new(config, pubkey_a, pubkey_b, 600_000, 400_000)?;
let funding_tx_id = channel.open()?;
assert!(funding_tx_id.starts_with("funding_"));
assert_eq!(channel.state, ChannelState::Open);
let signatures = vec!["sig_a".to_string(), "sig_b".to_string()];
let update = channel.update_state(500_000, 500_000, signatures)?;
assert_eq!(update.version, 1);
assert_eq!(update.balance_a, 500_000);
assert_eq!(update.balance_b, 500_000);
assert_eq!(channel.balance_a, 500_000);
assert_eq!(channel.balance_b, 500_000);
Ok(())
}
}