1use std::collections::HashMap;
7
8#[derive(Debug, Default)]
13pub struct ResourceTracker {
14 resources: HashMap<(Vec<u8>, String), bool>,
16}
17
18impl ResourceTracker {
19 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn exists(&self, address: &[u8], type_name: &str) -> bool {
25 self.resources
26 .get(&(address.to_vec(), type_name.to_string()))
27 .copied()
28 .unwrap_or(false)
29 }
30
31 pub fn move_to(&mut self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
35 let key = (address.to_vec(), type_name.to_string());
36 if self.resources.get(&key).copied().unwrap_or(false) {
37 return Err(ResourceError::AlreadyExists {
38 type_name: type_name.to_string(),
39 });
40 }
41 self.resources.insert(key, true);
42 Ok(())
43 }
44
45 pub fn move_from(&mut self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
49 let key = (address.to_vec(), type_name.to_string());
50 if !self.resources.get(&key).copied().unwrap_or(false) {
51 return Err(ResourceError::NotFound {
52 type_name: type_name.to_string(),
53 });
54 }
55 self.resources.insert(key, false);
56 Ok(())
57 }
58
59 pub fn borrow(&self, address: &[u8], type_name: &str) -> Result<(), ResourceError> {
61 if !self.exists(address, type_name) {
62 return Err(ResourceError::NotFound {
63 type_name: type_name.to_string(),
64 });
65 }
66 Ok(())
67 }
68}
69
70#[derive(Debug, Clone, thiserror::Error)]
72pub enum ResourceError {
73 #[error("resource '{type_name}' already exists at address")]
74 AlreadyExists { type_name: String },
75
76 #[error("resource '{type_name}' not found at address")]
77 NotFound { type_name: String },
78
79 #[error("resource '{type_name}' cannot be copied")]
80 CannotCopy { type_name: String },
81
82 #[error("resource '{type_name}' cannot be dropped")]
83 CannotDrop { type_name: String },
84}
85
86pub fn generate_resource_tracker_code() -> Vec<u8> {
90 Vec::new()
95}
96
97pub fn signer_to_checkwitness() -> &'static str {
102 "System.Runtime.CheckWitness"
103}
104
105const RESOURCE_PREFIX: u8 = b'R';
107
108const RESOURCE_SEPARATOR: u8 = b':';
110
111pub fn global_storage_key(address: &[u8], type_name: &str) -> Vec<u8> {
116 let mut key = Vec::with_capacity(address.len() + type_name.len() + 2);
117 key.push(RESOURCE_PREFIX);
118 key.extend_from_slice(address);
119 key.push(RESOURCE_SEPARATOR);
120 key.extend_from_slice(type_name.as_bytes());
121 key
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_resource_lifecycle() {
130 let mut tracker = ResourceTracker::new();
131 let addr = b"test_address";
132 let type_name = "0x1::Coin::Coin";
133
134 assert!(!tracker.exists(addr, type_name));
136
137 tracker.move_to(addr, type_name).unwrap();
139 assert!(tracker.exists(addr, type_name));
140
141 assert!(tracker.move_to(addr, type_name).is_err());
143
144 tracker.move_from(addr, type_name).unwrap();
146 assert!(!tracker.exists(addr, type_name));
147
148 assert!(tracker.move_from(addr, type_name).is_err());
150 }
151}