phos_data_network_precompiles/
lib.rs1use std::fmt::Display;
4
5use alloy_primitives::{address, Address, Bytes};
6use revm::{
7 context::{Cfg, ContextTr, JournalTr, LocalContextTr},
8 handler::{ContextTrDbError, EthPrecompiles, PrecompileProvider},
9 interpreter::{CallInputs, CallScheme, Gas, InstructionResult, InterpreterResult},
10 precompile::{secp256r1, PrecompileError, PrecompileResult},
11 primitives::hardfork::SpecId,
12};
13
14pub mod error;
15pub mod ip_graph;
16pub mod storage;
17
18pub use error::{DataNetworkPrecompileError, Result};
19
20use ip_graph::{IpGraph, IP_GRAPH_ADDRESS};
21use storage::{evm::EvmPrecompileStorageProvider, StorageCtx};
22
23const P256_VERIFY_ADDRESS: Address = address!("0000000000000000000000000000000000000100");
24
25pub trait Precompile {
27 fn call(
29 &mut self,
30 calldata: &[u8],
31 msg_sender: Address,
32 call_scheme: CallScheme,
33 ) -> PrecompileResult;
34}
35
36#[derive(Debug, Clone, Default)]
38pub struct DataNetworkPrecompiles {
39 inner: EthPrecompiles,
40}
41
42impl<CTX> PrecompileProvider<CTX> for DataNetworkPrecompiles
43where
44 CTX: ContextTr<Cfg: Cfg<Spec = SpecId>>,
45 ContextTrDbError<CTX>: Display,
46{
47 type Output = InterpreterResult;
48
49 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
50 <EthPrecompiles as PrecompileProvider<CTX>>::set_spec(&mut self.inner, spec)
51 }
52
53 fn run(
54 &mut self,
55 context: &mut CTX,
56 inputs: &CallInputs,
57 ) -> std::result::Result<Option<Self::Output>, String> {
58 let result = if inputs.bytecode_address == P256_VERIFY_ADDRESS
59 && self.inner.spec >= SpecId::CANCUN
60 && self.inner.spec < SpecId::OSAKA
61 {
62 let calldata = inputs.input.bytes(context);
63 secp256r1::P256VERIFY.execute(&calldata, inputs.gas_limit)
64 } else if inputs.bytecode_address == IP_GRAPH_ADDRESS && self.inner.spec >= SpecId::CANCUN {
65 let calldata = inputs.input.bytes(context);
66 let required_gas = IpGraph::default().required_gas(&calldata);
67
68 if required_gas > inputs.gas_limit {
69 Err(PrecompileError::OutOfGas)
70 } else {
71 let mut storage = EvmPrecompileStorageProvider::new(context, inputs.is_static);
72 StorageCtx::enter(&mut storage, || {
73 IpGraph::default().call(&calldata, inputs.caller, inputs.scheme)
74 })
75 .map(|mut output| {
76 output.gas_used = required_gas;
77 output
78 })
79 }
80 } else {
81 return <EthPrecompiles as PrecompileProvider<CTX>>::run(
82 &mut self.inner,
83 context,
84 inputs,
85 );
86 };
87
88 let mut interpreter_result = InterpreterResult {
89 result: InstructionResult::Return,
90 gas: Gas::new(inputs.gas_limit),
91 output: Bytes::new(),
92 };
93
94 match result {
95 Ok(output) => {
96 interpreter_result.gas.record_refund(output.gas_refunded);
97 let recorded = interpreter_result.gas.record_cost(output.gas_used);
98 assert!(recorded, "Gas underflow is not possible");
99 interpreter_result.result = if output.reverted {
100 InstructionResult::Revert
101 } else {
102 InstructionResult::Return
103 };
104 interpreter_result.output = output.bytes;
105 }
106 Err(PrecompileError::Fatal(error)) => return Err(error),
107 Err(error) => {
108 interpreter_result.result = if error.is_oog() {
109 InstructionResult::PrecompileOOG
110 } else {
111 InstructionResult::PrecompileError
112 };
113 if !error.is_oog() && context.journal().depth() == 1 {
114 context
115 .local_mut()
116 .set_precompile_error_context(error.to_string());
117 }
118 }
119 }
120
121 Ok(Some(interpreter_result))
122 }
123
124 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
125 let p256_verify = (self.inner.spec >= SpecId::CANCUN && self.inner.spec < SpecId::OSAKA)
126 .then_some(P256_VERIFY_ADDRESS)
127 .into_iter();
128 let ip_graph = (self.inner.spec >= SpecId::CANCUN)
129 .then_some(IP_GRAPH_ADDRESS)
130 .into_iter();
131
132 Box::new(
133 self.inner
134 .warm_addresses()
135 .chain(p256_verify)
136 .chain(ip_graph),
137 )
138 }
139
140 fn contains(&self, address: &Address) -> bool {
141 (*address == IP_GRAPH_ADDRESS && self.inner.spec >= SpecId::CANCUN)
142 || (*address == P256_VERIFY_ADDRESS
143 && self.inner.spec >= SpecId::CANCUN
144 && self.inner.spec < SpecId::OSAKA)
145 || self.inner.contains(address)
146 }
147}