Skip to main content

phos_data_network_precompiles/ip_graph/
mod.rs

1//! DATA Network IP Graph precompile.
2
3pub mod dispatch;
4
5use std::collections::{HashMap, HashSet};
6
7use alloy_primitives::{address, b256, keccak256, Address, Keccak256, B256, U256};
8use alloy_sol_types::SolValue;
9use num_bigint::BigUint;
10
11use crate::{storage::StorageCtx, DataNetworkPrecompileError, Result};
12
13pub const IP_GRAPH_ADDRESS: Address = address!("0000000000000000000000000000000000000101");
14
15const ACL_ADDRESS: Address = address!("1640A22a8A086747cD377b73954545e2Dfcc9Cad");
16const ACL_SLOT: B256 = b256!("af99b37fdaacca72ee7240cb1435cc9e498aee6ef4edc19c8cc0cd787f4e6800");
17const HUNDRED_PERCENT: u64 = 100_000_000;
18
19#[derive(Debug, Default)]
20pub struct IpGraph {
21    pub(crate) storage: StorageCtx,
22}
23
24impl IpGraph {
25    fn is_allowed(&self, caller: Address) -> Result<bool> {
26        let key = U256::from_be_bytes(keccak256((caller, ACL_SLOT).abi_encode_packed()).0);
27        Ok(self.storage.sload(ACL_ADDRESS, key)? == U256::ONE)
28    }
29
30    pub fn add_parent_ip(
31        &mut self,
32        msg_sender: Address,
33        ip_id: Address,
34        parent_ip_ids: Vec<Address>,
35    ) -> Result<()> {
36        if !self.is_allowed(msg_sender)? {
37            return Err(DataNetworkPrecompileError::Unauthorized(
38                "caller not allowed to add parent IP",
39            ));
40        }
41
42        let data_slot = U256::from_be_bytes(keccak256(ip_id).0);
43
44        for (index, parent_ip_id) in parent_ip_ids.iter().enumerate() {
45            self.storage.sstore(
46                IP_GRAPH_ADDRESS,
47                data_slot + U256::from(index),
48                U256::from_be_slice(parent_ip_id.as_slice()),
49            )?;
50        }
51
52        self.storage.sstore(
53            IP_GRAPH_ADDRESS,
54            U256::from_be_slice(ip_id.as_slice()),
55            U256::from(parent_ip_ids.len()),
56        )
57    }
58
59    pub fn has_parent_ip(
60        &self,
61        msg_sender: Address,
62        ip_id: Address,
63        parent_ip_id: Address,
64    ) -> Result<bool> {
65        if !self.is_allowed(msg_sender)? {
66            return Err(DataNetworkPrecompileError::Unauthorized(
67                "caller not allowed to query hasParentIp",
68            ));
69        }
70
71        let length_slot = U256::from_be_slice(ip_id.as_slice());
72        let current_length = self.storage.sload(IP_GRAPH_ADDRESS, length_slot)?;
73        let data_slot = U256::from_be_bytes(keccak256(ip_id).0);
74
75        for index in 0..current_length.to::<u64>() {
76            let stored_parent = self
77                .storage
78                .sload(IP_GRAPH_ADDRESS, data_slot + U256::from(index))?;
79
80            if Address::from_word(B256::from(stored_parent)) == parent_ip_id {
81                return Ok(true);
82            }
83        }
84
85        Ok(false)
86    }
87
88    pub fn get_parent_ips(&self, msg_sender: Address, ip_id: Address) -> Result<Vec<Address>> {
89        if !self.is_allowed(msg_sender)? {
90            return Err(DataNetworkPrecompileError::Unauthorized(
91                "caller not allowed to query getParentIps",
92            ));
93        }
94
95        let length_slot = U256::from_be_slice(ip_id.as_slice());
96        let current_length = self.storage.sload(IP_GRAPH_ADDRESS, length_slot)?;
97        let data_slot = U256::from_be_bytes(keccak256(ip_id).0);
98        let mut parent_ip_ids = Vec::new();
99
100        for index in 0..current_length.to::<u64>() {
101            let stored_parent = self
102                .storage
103                .sload(IP_GRAPH_ADDRESS, data_slot + U256::from(index))?;
104
105            parent_ip_ids.push(Address::from_word(B256::from(stored_parent)));
106        }
107
108        Ok(parent_ip_ids)
109    }
110
111    pub fn get_parent_ips_count(&self, msg_sender: Address, ip_id: Address) -> Result<U256> {
112        if !self.is_allowed(msg_sender)? {
113            return Err(DataNetworkPrecompileError::Unauthorized(
114                "caller not allowed to query parent Ips count",
115            ));
116        }
117
118        self.storage
119            .sload(IP_GRAPH_ADDRESS, U256::from_be_slice(ip_id.as_slice()))
120    }
121
122    pub fn get_ancestor_ips(&self, msg_sender: Address, ip_id: Address) -> Result<Vec<Address>> {
123        if !self.is_allowed(msg_sender)? {
124            return Err(DataNetworkPrecompileError::Unauthorized(
125                "caller not allowed to query getAncestorIps",
126            ));
127        }
128
129        let mut ancestors: Vec<_> = self.find_ancestors(ip_id)?.into_iter().collect();
130        ancestors.sort_unstable();
131
132        Ok(ancestors)
133    }
134
135    pub fn get_ancestor_ips_count(&self, msg_sender: Address, ip_id: Address) -> Result<U256> {
136        if !self.is_allowed(msg_sender)? {
137            return Err(DataNetworkPrecompileError::Unauthorized(
138                "caller not allowed to query getAncestorIpsCount",
139            ));
140        }
141
142        Ok(U256::from(self.find_ancestors(ip_id)?.len()))
143    }
144
145    pub fn has_ancestor_ip(
146        &self,
147        msg_sender: Address,
148        ip_id: Address,
149        ancestor_ip_id: Address,
150    ) -> Result<bool> {
151        if !self.is_allowed(msg_sender)? {
152            return Err(DataNetworkPrecompileError::Unauthorized(
153                "caller not allowed to query hasAncestorIp",
154            ));
155        }
156
157        Ok(self.find_ancestors(ip_id)?.contains(&ancestor_ip_id))
158    }
159
160    fn find_ancestors(&self, ip_id: Address) -> Result<HashSet<Address>> {
161        let mut ancestors = HashSet::new();
162        let mut stack = vec![ip_id];
163
164        while let Some(node) = stack.pop() {
165            let current_length = self
166                .storage
167                .sload(IP_GRAPH_ADDRESS, U256::from_be_slice(node.as_slice()))?;
168            let data_slot = U256::from_be_bytes(keccak256(node).0);
169
170            for index in 0..current_length.to::<u64>() {
171                let stored_parent = self
172                    .storage
173                    .sload(IP_GRAPH_ADDRESS, data_slot + U256::from(index))?;
174                let parent_ip_id = Address::from_word(B256::from(stored_parent));
175
176                if ancestors.insert(parent_ip_id) {
177                    stack.push(parent_ip_id);
178                }
179            }
180        }
181
182        Ok(ancestors)
183    }
184
185    pub fn set_royalty(
186        &mut self,
187        msg_sender: Address,
188        ip_id: Address,
189        parent_ip_id: Address,
190        royalty_policy_kind: U256,
191        royalty: U256,
192    ) -> Result<()> {
193        if !self.is_allowed(msg_sender)? {
194            return Err(DataNetworkPrecompileError::Unauthorized(
195                "caller not allowed to set Royalty",
196            ));
197        }
198
199        if royalty > U256::from(u32::MAX) {
200            return Err(DataNetworkPrecompileError::Revert(
201                "royalty value exceeds uint32 range",
202            ));
203        }
204
205        let policy_bytes = royalty_policy_kind.to_be_bytes_trimmed_vec();
206        let mut hasher = Keccak256::new();
207        hasher.update(ip_id.as_slice());
208        hasher.update(parent_ip_id.as_slice());
209        hasher.update(&policy_bytes);
210        let slot = U256::from_be_bytes(hasher.finalize().0);
211        self.storage.sstore(IP_GRAPH_ADDRESS, slot, royalty)?;
212
213        if royalty_policy_kind == U256::ZERO {
214            let mut hasher = Keccak256::new();
215            hasher.update(parent_ip_id.as_slice());
216            hasher.update(&policy_bytes);
217            hasher.update(b"royaltyStack");
218            let parent_slot = U256::from_be_bytes(hasher.finalize().0);
219            let parent_royalty_stack = self.storage.sload(IP_GRAPH_ADDRESS, parent_slot)?;
220
221            let mut hasher = Keccak256::new();
222            hasher.update(ip_id.as_slice());
223            hasher.update(&policy_bytes);
224            hasher.update(b"royaltyStack");
225            let royalty_stack_slot = U256::from_be_bytes(hasher.finalize().0);
226            let royalty_stack = self.storage.sload(IP_GRAPH_ADDRESS, royalty_stack_slot)?;
227            let royalty_stack = royalty_stack
228                .wrapping_add(parent_royalty_stack)
229                .wrapping_add(royalty);
230
231            self.storage
232                .sstore(IP_GRAPH_ADDRESS, royalty_stack_slot, royalty_stack)?;
233        }
234
235        Ok(())
236    }
237
238    pub fn get_royalty(
239        &self,
240        msg_sender: Address,
241        ip_id: Address,
242        ancestor_ip_id: Address,
243        royalty_policy_kind: U256,
244    ) -> Result<U256> {
245        if !self.is_allowed(msg_sender)? {
246            return Err(DataNetworkPrecompileError::Unauthorized(
247                "caller not allowed to query getRoyalty",
248            ));
249        }
250
251        let total_royalty = match royalty_policy_kind {
252            U256::ZERO => self.get_royalty_lap(ip_id, ancestor_ip_id)?,
253            U256::ONE => self.get_royalty_lrp(ip_id, ancestor_ip_id)?,
254            _ => {
255                return Err(DataNetworkPrecompileError::Revert(
256                    "unknown royalty policy kind",
257                ));
258            }
259        };
260
261        if total_royalty > BigUint::from(u32::MAX) {
262            return Err(DataNetworkPrecompileError::Revert(
263                "royalty value exceeds uint32 range",
264            ));
265        }
266
267        Ok(U256::from_be_slice(&total_royalty.to_bytes_be()))
268    }
269
270    fn get_royalty_lap(&self, ip_id: Address, ancestor_ip_id: Address) -> Result<BigUint> {
271        let mut royalties = HashMap::new();
272        let mut path_counts = HashMap::new();
273        royalties.insert(ip_id, BigUint::from(HUNDRED_PERCENT));
274        path_counts.insert(ip_id, BigUint::from(1u8));
275
276        let (topo_order, all_parents) = self.topological_sort(ip_id, ancestor_ip_id)?;
277        let policy_bytes = U256::ZERO.to_be_bytes_trimmed_vec();
278
279        for node in topo_order.into_iter().rev() {
280            if node == ancestor_ip_id {
281                break;
282            }
283
284            let Some(parents) = all_parents.get(&node) else {
285                continue;
286            };
287            let contribution = path_counts.get(&node).cloned().ok_or_else(|| {
288                DataNetworkPrecompileError::Fatal(
289                    "missing path count while calculating LAP royalty".into(),
290                )
291            })?;
292
293            for parent_ip_id in parents {
294                let mut hasher = Keccak256::new();
295                hasher.update(node.as_slice());
296                hasher.update(parent_ip_id.as_slice());
297                hasher.update(&policy_bytes);
298                let royalty_slot = U256::from_be_bytes(hasher.finalize().0);
299                let parent_royalty = self.storage.sload(IP_GRAPH_ADDRESS, royalty_slot)?;
300                let parent_royalty = BigUint::from_bytes_be(&parent_royalty.to_be_bytes::<32>());
301
302                *path_counts.entry(*parent_ip_id).or_default() += &contribution;
303                *royalties.entry(*parent_ip_id).or_default() += &contribution * parent_royalty;
304            }
305        }
306
307        Ok(royalties.remove(&ancestor_ip_id).unwrap_or_default())
308    }
309
310    fn get_royalty_lrp(&self, ip_id: Address, ancestor_ip_id: Address) -> Result<BigUint> {
311        let mut royalties = HashMap::new();
312        royalties.insert(ip_id, BigUint::from(HUNDRED_PERCENT));
313
314        let (topo_order, all_parents) = self.topological_sort(ip_id, ancestor_ip_id)?;
315        let policy_bytes = U256::ONE.to_be_bytes_trimmed_vec();
316
317        for node in topo_order.into_iter().rev() {
318            if node == ancestor_ip_id {
319                break;
320            }
321
322            let current_royalty = royalties.get(&node).cloned().unwrap_or_default();
323            if current_royalty == BigUint::default() {
324                continue;
325            }
326
327            let Some(parents) = all_parents.get(&node) else {
328                continue;
329            };
330
331            for parent_ip_id in parents {
332                let mut hasher = Keccak256::new();
333                hasher.update(node.as_slice());
334                hasher.update(parent_ip_id.as_slice());
335                hasher.update(&policy_bytes);
336                let royalty_slot = U256::from_be_bytes(hasher.finalize().0);
337                let parent_royalty = self.storage.sload(IP_GRAPH_ADDRESS, royalty_slot)?;
338                let parent_royalty = BigUint::from_bytes_be(&parent_royalty.to_be_bytes::<32>());
339                let contribution =
340                    &current_royalty * parent_royalty / BigUint::from(HUNDRED_PERCENT);
341
342                *royalties.entry(*parent_ip_id).or_default() += contribution;
343            }
344        }
345
346        Ok(royalties.remove(&ancestor_ip_id).unwrap_or_default())
347    }
348
349    #[allow(clippy::type_complexity)]
350    fn topological_sort(
351        &self,
352        ip_id: Address,
353        ancestor_ip_id: Address,
354    ) -> Result<(Vec<Address>, HashMap<Address, Vec<Address>>)> {
355        let mut all_parents = HashMap::<Address, Vec<Address>>::new();
356        let mut visited = HashSet::new();
357        let mut in_topo_order = HashSet::new();
358        let mut topo_order = Vec::new();
359        let mut stack = vec![ip_id];
360
361        while let Some(current) = stack.pop() {
362            if visited.contains(&current) {
363                if in_topo_order.insert(current) {
364                    topo_order.push(current);
365                }
366                continue;
367            }
368
369            visited.insert(current);
370            stack.push(current);
371
372            let current_length = self
373                .storage
374                .sload(IP_GRAPH_ADDRESS, U256::from_be_slice(current.as_slice()))?;
375            let data_slot = U256::from_be_bytes(keccak256(current).0);
376
377            for index in 0..current_length.to::<u64>() {
378                let stored_parent = self
379                    .storage
380                    .sload(IP_GRAPH_ADDRESS, data_slot + U256::from(index))?;
381                let parent_ip_id = Address::from_word(B256::from(stored_parent));
382                all_parents.entry(current).or_default().push(parent_ip_id);
383
384                if !visited.contains(&parent_ip_id) {
385                    stack.push(parent_ip_id);
386                }
387            }
388        }
389
390        if !visited.contains(&ancestor_ip_id) {
391            return Ok((Vec::new(), HashMap::new()));
392        }
393
394        Ok((topo_order, all_parents))
395    }
396
397    pub fn get_royalty_stack(
398        &self,
399        msg_sender: Address,
400        ip_id: Address,
401        royalty_policy_kind: U256,
402    ) -> Result<U256> {
403        if !self.is_allowed(msg_sender)? {
404            return Err(DataNetworkPrecompileError::Unauthorized(
405                "caller not allowed to query getRoyaltyStack",
406            ));
407        }
408
409        match royalty_policy_kind {
410            U256::ZERO => self.get_royalty_stack_lap(ip_id),
411            U256::ONE => self.get_royalty_stack_lrp(ip_id),
412            _ => Err(DataNetworkPrecompileError::Revert(
413                "unknown royalty policy kind",
414            )),
415        }
416    }
417
418    fn get_royalty_stack_lap(&self, ip_id: Address) -> Result<U256> {
419        let policy_bytes = U256::ZERO.to_be_bytes_trimmed_vec();
420        let mut hasher = Keccak256::new();
421        hasher.update(ip_id.as_slice());
422        hasher.update(&policy_bytes);
423        hasher.update(b"royaltyStack");
424        let slot = U256::from_be_bytes(hasher.finalize().0);
425
426        self.storage.sload(IP_GRAPH_ADDRESS, slot)
427    }
428
429    fn get_royalty_stack_lrp(&self, ip_id: Address) -> Result<U256> {
430        let current_length = self
431            .storage
432            .sload(IP_GRAPH_ADDRESS, U256::from_be_slice(ip_id.as_slice()))?;
433        let data_slot = U256::from_be_bytes(keccak256(ip_id).0);
434        let policy_bytes = U256::ONE.to_be_bytes_trimmed_vec();
435        let mut total_royalty = U256::ZERO;
436
437        for index in 0..current_length.to::<u64>() {
438            let stored_parent = self
439                .storage
440                .sload(IP_GRAPH_ADDRESS, data_slot + U256::from(index))?;
441            let parent_ip_id = Address::from_word(B256::from(stored_parent));
442
443            let mut hasher = Keccak256::new();
444            hasher.update(ip_id.as_slice());
445            hasher.update(parent_ip_id.as_slice());
446            hasher.update(&policy_bytes);
447            let royalty_slot = U256::from_be_bytes(hasher.finalize().0);
448            let royalty = self.storage.sload(IP_GRAPH_ADDRESS, royalty_slot)?;
449
450            total_royalty = total_royalty.wrapping_add(royalty);
451        }
452
453        Ok(total_royalty)
454    }
455}