phos_data_network_precompiles/ip_graph/
mod.rs1pub 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 ¤t_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(¤t) {
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}