Skip to main content

phos_data_network_precompiles/
lib.rs

1//! DATA Network precompile implementations.
2
3use 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
25/// Trait implemented by DATA Network precompile contract types.
26pub trait Precompile {
27    /// ABI-decodes calldata and dispatches it to the matching precompile method.
28    fn call(
29        &mut self,
30        calldata: &[u8],
31        msg_sender: Address,
32        call_scheme: CallScheme,
33    ) -> PrecompileResult;
34}
35
36/// Ethereum precompiles extended with DATA Network's stateful precompiles.
37#[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}