1#[cfg(feature = "rust-bitcoin")]
9use crate::bitcoin::wallet::Asset;
10use chrono;
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13use uuid::Uuid;
14#[cfg(feature = "rust-bitcoin")]
16use bitcoin::hashes::{Hash, HashEngine};
17#[cfg(feature = "rust-bitcoin")]
18use bitcoin::secp256k1::Secp256k1;
19#[cfg(feature = "rust-bitcoin")]
22use bitcoin::hashes::sha256;
23#[cfg(feature = "rust-bitcoin")]
25use hex;
26use serde::{Deserialize, Serialize};
27use thiserror::Error;
28
29#[cfg(not(feature = "rust-bitcoin"))]
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Asset {
33 pub id: String,
34 pub name: String,
35 pub amount: u64,
36 pub metadata: std::collections::HashMap<String, String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct AssetRegistryConfig {
43 pub storage_path: String,
44 pub network: String,
45}
46
47#[allow(dead_code)]
50#[derive(Debug)]
51pub struct AssetRegistry {
52 config: AssetRegistryConfig,
53 assets: Arc<Mutex<HashMap<String, RgbAsset>>>,
54 issuances: Arc<Mutex<HashMap<String, RgbIssuance>>>,
55 transfers: Arc<Mutex<HashMap<String, RgbTransfer>>>,
56}
57
58impl Clone for AssetRegistry {
59 fn clone(&self) -> Self {
60 Self {
61 config: self.config.clone(),
62 assets: Arc::clone(&self.assets),
63 issuances: Arc::clone(&self.issuances),
64 transfers: Arc::clone(&self.transfers),
65 }
66 }
67}
68
69impl AssetRegistry {
70 pub fn new(config: AssetRegistryConfig) -> Self {
73 Self {
74 config,
75 assets: Arc::new(Mutex::new(HashMap::new())),
76 issuances: Arc::new(Mutex::new(HashMap::new())),
77 transfers: Arc::new(Mutex::new(HashMap::new())),
78 }
79 }
80
81 pub async fn register_asset(&self, asset: &RgbAsset) -> RgbResult<()> {
84 let mut assets = self.assets.lock().unwrap();
85 assets.insert(asset.id.clone(), asset.clone());
86 Ok(())
87 }
88
89 pub async fn update_issuance(&self, issuance: &RgbIssuance) -> RgbResult<()> {
92 let mut issuances = self.issuances.lock().unwrap();
93 issuances.insert(issuance.asset_id.clone(), issuance.clone());
94 Ok(())
95 }
96
97 pub fn update_asset_from_transfer(
100 &mut self,
101 asset_id: &str,
102 transfer: &RgbTransfer,
103 ) -> RgbResult<()> {
104 let mut assets = self.assets.lock().unwrap();
105 if let Some(asset) = assets.get_mut(asset_id) {
106 asset.issued_supply += transfer.amount;
107 asset.updated_at = Some(transfer.created_at);
108 Ok(())
109 } else {
110 Err(RgbError::AssetNotFound)
111 }
112 }
113
114 pub async fn update_transfer(&self, transfer: &RgbTransfer) -> RgbResult<()> {
117 let mut transfers = self.transfers.lock().unwrap();
118 transfers.insert(transfer.asset_id.clone(), transfer.clone());
119 Ok(())
120 }
121
122 pub async fn register_external_asset(&mut self, _asset: Asset) -> Result<String, RgbError> {
124 let asset_id = format!("rgb_asset_{}", uuid::Uuid::new_v4());
125 Ok(asset_id)
127 }
128
129 pub async fn get_asset(
131 &self,
132 _asset_id: &str,
133 ) -> Result<Option<Asset>, Box<dyn std::error::Error + Send + Sync>> {
134 Ok(None)
136 }
137
138 pub async fn list_assets(
140 &self,
141 ) -> Result<Vec<Asset>, Box<dyn std::error::Error + Send + Sync>> {
142 Ok(Vec::new())
144 }
145}
146
147#[derive(Debug, Clone)]
150pub struct ContractManager {
151 #[allow(dead_code)] #[cfg(feature = "rust-bitcoin")]
153 secp: Secp256k1<bitcoin::secp256k1::All>,
154 #[cfg(not(feature = "rust-bitcoin"))]
155 _placeholder: (),
156}
157
158impl Default for ContractManager {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl ContractManager {
165 #[cfg(feature = "rust-bitcoin")]
168 fn generate_asset_id(
169 issuer_address: &str,
170 total_supply: u64,
171 precision: u8,
172 metadata: &str,
173 ) -> RgbResult<String> {
174 let mut engine = sha256::HashEngine::default();
177
178 engine.input(issuer_address.as_bytes());
180 engine.input(&total_supply.to_le_bytes());
181 engine.input(&[precision]);
182 engine.input(metadata.as_bytes());
183
184 let timestamp = chrono::Utc::now().timestamp();
187 engine.input(×tamp.to_le_bytes());
188
189 let hash = sha256::Hash::from_engine(engine);
191
192 let hex_string = hex::encode::<&[u8]>(hash.as_ref());
196 let asset_id = format!("rgb1{hex_string}");
197
198 Ok(asset_id)
199 }
200
201 #[cfg(not(feature = "rust-bitcoin"))]
203 fn generate_asset_id(
204 issuer_address: &str,
205 total_supply: u64,
206 precision: u8,
207 metadata: &str,
208 ) -> RgbResult<String> {
209 use std::collections::hash_map::DefaultHasher;
211 use std::hash::{Hash, Hasher};
212
213 let mut hasher = DefaultHasher::new();
214 issuer_address.hash(&mut hasher);
215 total_supply.hash(&mut hasher);
216 precision.hash(&mut hasher);
217 metadata.hash(&mut hasher);
218 chrono::Utc::now().timestamp().hash(&mut hasher);
219
220 let hash = hasher.finish();
221 Ok(format!("rgb1{:x}", hash))
222 }
223
224 #[cfg(feature = "rust-bitcoin")]
227 pub fn new() -> Self {
228 Self {
229 secp: Secp256k1::new(),
230 }
231 }
232
233 #[cfg(not(feature = "rust-bitcoin"))]
236 pub fn new() -> Self {
237 Self { _placeholder: () }
238 }
239
240 pub fn create_asset(
243 &self,
244 issuer_address: &str,
245 total_supply: u64,
246 precision: u8,
247 metadata: &str,
248 ) -> RgbResult<RgbAsset> {
249 let asset_id = Self::generate_asset_id(issuer_address, total_supply, precision, metadata)?;
251
252 let mut metadata_map = HashMap::new();
254 metadata_map.insert("description".to_string(), metadata.to_string());
255 metadata_map.insert(
256 "tr_pattern".to_string(),
257 "tr(KEY,{SILENT_LEAF})".to_string(),
258 );
259
260 Ok(RgbAsset {
263 id: asset_id.clone(), asset_id,
265 ticker: format!("RGB{precision}"),
266 name: metadata.to_string(),
267 precision,
268 issued_supply: 0,
269 owner: issuer_address.to_string(),
270 created_at: chrono::Utc::now().timestamp() as u64,
271 metadata: metadata_map,
272 updated_at: None,
273 })
274 }
275
276 pub fn issue_asset(&self, issuance_address: &str, amount: u64) -> RgbResult<RgbIssuance> {
279 Ok(RgbIssuance {
281 asset_id: "asset_placeholder".to_string(), issuer: issuance_address.to_string(),
283 amount,
284 timestamp: chrono::Utc::now().timestamp() as u64,
285 status: IssuanceStatus::Pending,
286 })
287 }
288
289 pub fn transfer_asset(
292 &self,
293 sender_address: &str,
294 recipient_address: &str,
295 amount: u64,
296 ) -> RgbResult<RgbTransfer> {
297 Ok(RgbTransfer {
299 asset_id: "asset_placeholder".to_string(), amount,
301 from: sender_address.to_string(),
302 to: recipient_address.to_string(),
303 fee: 1000, created_at: chrono::Utc::now().timestamp() as u64,
305 updated_at: None,
306 status: Some("pending".to_string()),
307 txid: None,
308 nonce: Uuid::new_v4().to_string(),
309 signature: None,
310 metadata: HashMap::new(),
311 version: "1.0".to_string(),
312 network: "bitcoin".to_string(),
313 })
314 }
315}
316
317#[derive(Debug, Error)]
320pub enum RgbError {
321 #[error("Invalid asset ID")]
322 InvalidAssetId,
323 #[error("Insufficient funds")]
324 InsufficientFunds,
325 #[error("Invalid transaction")]
326 InvalidTransaction,
327 #[error("Asset already exists")]
328 AssetAlreadyExists,
329 #[error("Asset not found")]
330 AssetNotFound,
331 #[error("Bitcoin error: {0}")]
332 BitcoinError(String),
333 #[error("IO error")]
334 IoError(#[from] std::io::Error),
335 #[error("Serialization error: {0}")]
336 SerializationError(String),
337 #[error("Network error: {0}")]
338 NetworkError(String),
339}
340
341#[cfg(feature = "rust-bitcoin")]
342impl From<bitcoin::consensus::encode::Error> for RgbError {
343 fn from(err: bitcoin::consensus::encode::Error) -> Self {
344 RgbError::SerializationError(err.to_string())
345 }
346}
347
348pub type RgbResult<T> = Result<T, RgbError>;
351
352#[cfg(feature = "rust-bitcoin")]
355pub fn generate_asset_id(
356 issuer_address: &str,
357 total_supply: u64,
358 precision: u8,
359 metadata: &str,
360) -> RgbResult<String> {
361 let mut engine = sha256::HashEngine::default();
364
365 engine.input(issuer_address.as_bytes());
367 engine.input(&total_supply.to_le_bytes());
368 engine.input(&[precision]);
369 engine.input(metadata.as_bytes());
370
371 let timestamp = chrono::Utc::now().timestamp();
374 engine.input(×tamp.to_le_bytes());
375
376 let hash = sha256::Hash::from_engine(engine);
378
379 let hex_string = hex::encode::<&[u8]>(hash.as_ref());
383 let asset_id = format!("rgb1{hex_string}");
384
385 Ok(asset_id)
386}
387
388#[cfg(not(feature = "rust-bitcoin"))]
390pub fn generate_asset_id(
391 issuer_address: &str,
392 total_supply: u64,
393 precision: u8,
394 metadata: &str,
395) -> RgbResult<String> {
396 use std::collections::hash_map::DefaultHasher;
398 use std::hash::{Hash, Hasher};
399
400 let mut hasher = DefaultHasher::new();
401 issuer_address.hash(&mut hasher);
402 total_supply.hash(&mut hasher);
403 precision.hash(&mut hasher);
404 metadata.hash(&mut hasher);
405 chrono::Utc::now().timestamp().hash(&mut hasher);
406
407 let hash = hasher.finish();
408 Ok(format!("rgb1{:x}", hash))
409}
410
411#[derive(Serialize, Deserialize, Debug, Clone)]
413pub struct RgbAsset {
414 pub id: String, pub asset_id: String, pub ticker: String, pub name: String, pub precision: u8, pub issued_supply: u64, pub owner: String, pub created_at: u64, pub metadata: HashMap<String, String>, #[serde(skip_serializing_if = "Option::is_none")]
424 pub updated_at: Option<u64>, }
426
427#[derive(Serialize, Deserialize, Debug, Clone)]
429pub struct RgbIssuance {
430 pub asset_id: String,
431 pub issuer: String,
432 pub amount: u64,
433 pub timestamp: u64,
434 pub status: IssuanceStatus,
435}
436
437#[derive(Serialize, Deserialize, Debug, Clone)]
439pub struct RgbTransfer {
440 pub asset_id: String,
441 pub amount: u64,
442 pub from: String,
443 pub to: String,
444 pub fee: u64,
445 pub created_at: u64,
446 pub updated_at: Option<u64>,
447 pub status: Option<String>,
448 pub txid: Option<String>,
449 pub nonce: String,
450 pub signature: Option<String>,
451 pub metadata: HashMap<String, String>,
452 pub version: String,
453 pub network: String,
454}
455
456#[derive(Serialize, Deserialize, Debug, Clone)]
458pub enum AssetStatus {
459 Created,
460 Issued,
461 Transferring,
462 Active,
463 Frozen,
464}
465
466#[derive(Serialize, Deserialize, Debug, Clone)]
468pub enum IssuanceStatus {
469 Pending,
470 Confirmed,
471 Failed,
472}
473
474#[derive(Serialize, Deserialize, Debug, Clone)]
476pub enum TransferStatus {
477 Pending,
478 Confirmed,
479 Failed,
480}
481
482use crate::layer2::{
484 create_protocol_state, create_validation_result, create_verification_result, AssetParams,
485 AssetTransfer, Layer2Protocol, Proof, ProtocolState, TransactionStatus, TransferResult,
486 ValidationResult, VerificationResult,
487};
488use async_trait::async_trait;
489
490#[derive(Debug, Clone)]
493pub struct RgbProtocol {
494 asset_registry: AssetRegistry,
495 contract_manager: ContractManager,
496}
497
498impl RgbProtocol {
499 pub fn new() -> Self {
500 let config = AssetRegistryConfig {
501 storage_path: "/tmp/rgb_assets".to_string(),
502 network: "bitcoin".to_string(),
503 };
504
505 Self {
506 asset_registry: AssetRegistry::new(config),
507 contract_manager: ContractManager::new(),
508 }
509 }
510
511 pub fn get_asset_registry(&self) -> &AssetRegistry {
513 &self.asset_registry
514 }
515
516 pub fn get_asset_registry_mut(&mut self) -> &mut AssetRegistry {
518 &mut self.asset_registry
519 }
520
521 pub async fn register_asset(
523 &mut self,
524 asset: Asset,
525 ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
526 self.asset_registry
527 .register_external_asset(asset)
528 .await
529 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
530 }
531
532 pub async fn get_asset(
534 &self,
535 asset_id: &str,
536 ) -> Result<Option<Asset>, Box<dyn std::error::Error + Send + Sync>> {
537 self.asset_registry.get_asset(asset_id).await
538 }
539
540 pub async fn list_assets(
542 &self,
543 ) -> Result<Vec<Asset>, Box<dyn std::error::Error + Send + Sync>> {
544 self.asset_registry.list_assets().await
545 }
546}
547
548impl Default for RgbProtocol {
549 fn default() -> Self {
550 Self::new()
551 }
552}
553
554#[async_trait]
555impl Layer2Protocol for RgbProtocol {
556 async fn initialize(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
557 Ok(())
559 }
560
561 async fn connect(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
562 Ok(())
564 }
565
566 async fn get_state(&self) -> Result<ProtocolState, Box<dyn std::error::Error + Send + Sync>> {
567 Ok(create_protocol_state("1.0", 0, None, true))
568 }
569
570 async fn submit_transaction(
571 &self,
572 _tx_data: &[u8],
573 ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
574 let tx_id = format!("rgb_tx_{}", uuid::Uuid::new_v4());
575 Ok(tx_id)
576 }
577
578 async fn check_transaction_status(
579 &self,
580 _tx_id: &str,
581 ) -> Result<TransactionStatus, Box<dyn std::error::Error + Send + Sync>> {
582 use crate::layer2::TransactionStatus;
583 Ok(TransactionStatus::Confirmed)
584 }
585
586 async fn sync_state(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
587 Ok(())
589 }
590
591 async fn issue_asset(
592 &self,
593 params: AssetParams,
594 ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
595 let asset = self.contract_manager.create_asset(
596 ¶ms.metadata,
597 params.total_supply,
598 params.precision,
599 ¶ms.name,
600 )?;
601
602 Ok(asset.id)
603 }
604
605 async fn transfer_asset(
606 &self,
607 transfer: AssetTransfer,
608 ) -> Result<TransferResult, Box<dyn std::error::Error + Send + Sync>> {
609 use crate::layer2::{TransactionStatus, TransferResult};
610 let rgb_transfer =
611 self.contract_manager
612 .transfer_asset(&transfer.from, &transfer.to, transfer.amount)?;
613
614 Ok(TransferResult {
615 tx_id: rgb_transfer.nonce,
616 status: TransactionStatus::Pending,
617 fee: Some(rgb_transfer.fee),
618 timestamp: rgb_transfer.created_at,
619 })
620 }
621
622 async fn verify_proof(
623 &self,
624 _proof: Proof,
625 ) -> Result<VerificationResult, Box<dyn std::error::Error + Send + Sync>> {
626 Ok(create_verification_result(true, None))
628 }
629
630 async fn validate_state(
631 &self,
632 _state_data: &[u8],
633 ) -> Result<ValidationResult, Box<dyn std::error::Error + Send + Sync>> {
634 Ok(create_validation_result(true, vec![]))
636 }
637}