Skip to content

Commit 19346ec

Browse files
committed
Implement Data Processor module in cairo
1 parent cfb285a commit 19346ec

File tree

3 files changed

+254
-0
lines changed

3 files changed

+254
-0
lines changed

cairo/src/data_processor.cairo

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
use core::keccak::keccak_u256s_be_inputs;
2+
use cairo_lib::utils::bitwise::reverse_endianness_u256;
3+
4+
#[derive(Drop, Serde, starknet::Store, PartialEq)]
5+
enum TaskStatus {
6+
NONE,
7+
SCHEDULED,
8+
FINALIZED
9+
}
10+
11+
#[derive(Drop, Serde, starknet::Store)]
12+
struct TaskResult {
13+
status: TaskStatus,
14+
result: u256,
15+
}
16+
17+
#[derive(Drop, Serde)]
18+
struct MmrData {
19+
chain_id: u256,
20+
mmr_id: u256,
21+
mmr_size: u256,
22+
}
23+
24+
#[derive(Drop, Serde)]
25+
struct TaskData {
26+
mmr_data: Span<MmrData>,
27+
task_result_low: u128,
28+
task_result_high: u128,
29+
task_hash_low: u128,
30+
task_hash_high: u128,
31+
module_hash: u256,
32+
program_hash: felt252,
33+
}
34+
35+
#[derive(Drop, Serde)]
36+
struct ModuleTask {
37+
program_hash: u256,
38+
inputs: Span<u256>,
39+
}
40+
41+
#[starknet::interface]
42+
pub trait IDataProcessor<TContractState> {
43+
// ========================= Setup Functions ========================= //
44+
45+
/// Set the program hash for the HDP program
46+
fn setDataProcessorProgramHash(ref self: TContractState, program_hash: felt252);
47+
48+
/// Disable some program hashes
49+
fn disableProgramHashes(ref self: TContractState, program_hashes: Span<felt252>);
50+
51+
/// Checks if a program hash is currently authorized
52+
fn isProgramHashAuthorized(self: @TContractState, program_hash: felt252) -> bool;
53+
54+
// ========================= Core Functions ========================= //
55+
56+
/// Requests the execution of a task with a module
57+
fn requestDataProcessorExecutionOfTask(ref self: TContractState, module_task: ModuleTask);
58+
59+
/// Authenticates the execution of a task is finalized by verifying the locally computed fact with the FactsRegistry
60+
fn authenticateDataProcessorTaskExecution(ref self: TContractState, task_data: TaskData);
61+
62+
/// Returns the status of a task
63+
fn getDataProcessorTaskStatus(self: @TContractState, task_commitment: u256) -> TaskStatus;
64+
65+
/// Returns the result of a finalized task
66+
fn getDataProcessorFinalizedTaskResult(self: @TContractState, task_commitment: u256) -> u256;
67+
}
68+
69+
#[starknet::component]
70+
pub mod data_processor_component {
71+
use crate::{
72+
state::state_component,
73+
mmr_core::mmr_core_component::MmrCoreExternalImpl,
74+
mmr_core::POSEIDON_HASHING_FUNCTION,
75+
cairo_fact_registry::{cairo_fact_registry_component, ICairoFactRegistry},
76+
};
77+
use starknet::storage::{
78+
StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry, Map,
79+
};
80+
use openzeppelin::access::ownable::{
81+
OwnableComponent, OwnableComponent::InternalTrait as OwnableInternal,
82+
};
83+
use integrity::calculate_fact_hash;
84+
use super::*;
85+
86+
#[storage]
87+
struct Storage {
88+
cachedTasksResult: Map<u256, TaskResult>,
89+
authorizedProgramHashes: Map<felt252, bool>,
90+
}
91+
92+
#[derive(Drop, starknet::Event)]
93+
struct ProgramHashEnabled {
94+
program_hash: felt252,
95+
}
96+
97+
#[derive(Drop, starknet::Event)]
98+
struct ProgramHashesDisabled {
99+
program_hashes: Span<felt252>,
100+
}
101+
102+
#[derive(Drop, starknet::Event)]
103+
struct TaskAlreadyStored {
104+
task_commitment: u256,
105+
}
106+
107+
#[derive(Drop, starknet::Event)]
108+
struct ModuleTaskScheduled {
109+
module_task: ModuleTask,
110+
}
111+
112+
#[derive(Drop, starknet::Event)]
113+
struct TaskFinalized {
114+
task_hash: u256,
115+
task_result: u256,
116+
}
117+
118+
#[event]
119+
#[derive(Drop, starknet::Event)]
120+
pub enum Event {
121+
ProgramHashEnabled: ProgramHashEnabled,
122+
ProgramHashesDisabled: ProgramHashesDisabled,
123+
TaskAlreadyStored: TaskAlreadyStored,
124+
ModuleTaskScheduled: ModuleTaskScheduled,
125+
TaskFinalized: TaskFinalized,
126+
}
127+
128+
#[embeddable_as(DataProcessor)]
129+
impl DataProcessorImpl<
130+
TContractState,
131+
+HasComponent<TContractState>,
132+
+Drop<TContractState>,
133+
impl State: state_component::HasComponent<TContractState>,
134+
impl Ownable: OwnableComponent::HasComponent<TContractState>,
135+
impl CairoFactRegistry: cairo_fact_registry_component::HasComponent<TContractState>,
136+
> of IDataProcessor<ComponentState<TContractState>> {
137+
// ========================= Setup Functions ========================= //
138+
139+
fn setDataProcessorProgramHash(ref self: ComponentState<TContractState>, program_hash: felt252) {
140+
get_dep_component!(@self, Ownable).assert_only_owner();
141+
142+
self.authorizedProgramHashes.entry(program_hash).write(true);
143+
self.emit(Event::ProgramHashEnabled(ProgramHashEnabled {program_hash}))
144+
}
145+
146+
fn disableProgramHashes(ref self: ComponentState<TContractState>, program_hashes: Span<felt252>) {
147+
get_dep_component!(@self, Ownable).assert_only_owner();
148+
149+
for program_hash in program_hashes {
150+
self.authorizedProgramHashes.entry(*program_hash).write(false);
151+
};
152+
self.emit(Event::ProgramHashesDisabled(ProgramHashesDisabled{program_hashes}));
153+
}
154+
155+
fn isProgramHashAuthorized(self: @ComponentState<TContractState>, program_hash: felt252) -> bool {
156+
self.authorizedProgramHashes.entry(program_hash).read()
157+
}
158+
159+
// ========================= Core Functions ========================= //
160+
161+
fn requestDataProcessorExecutionOfTask(ref self: ComponentState<TContractState>, module_task: ModuleTask) {
162+
let mut keccak_input = array![module_task.program_hash];
163+
for input in module_task.inputs {
164+
keccak_input.append(*input);
165+
};
166+
let task_commitment = reverse_endianness_u256(keccak_u256s_be_inputs(keccak_input.span()));
167+
168+
let cached_result = self.cachedTasksResult.entry(task_commitment);
169+
let cached_result_status = cached_result.status.read();
170+
if cached_result_status == TaskStatus::FINALIZED {
171+
self.emit(Event::TaskAlreadyStored(TaskAlreadyStored {task_commitment} ));
172+
} else {
173+
// Ensure task is not already scheduled
174+
assert(cached_result_status == TaskStatus::NONE, 'DOUBLE_REGISTRATION');
175+
176+
// Store the task result
177+
cached_result.write(TaskResult{status: TaskStatus::SCHEDULED, result: 0});
178+
179+
self.emit(Event::ModuleTaskScheduled(ModuleTaskScheduled {module_task} ));
180+
}
181+
}
182+
183+
fn authenticateDataProcessorTaskExecution(ref self: ComponentState<TContractState>, task_data: TaskData) {
184+
let task_hash = u256 {low: task_data.task_hash_low, high: task_data.task_hash_high };
185+
186+
assert(self.cachedTasksResult.entry(task_hash).status.read() != TaskStatus::FINALIZED, 'TaskAlreadyFinalized');
187+
188+
assert(self.isProgramHashAuthorized(task_data.program_hash), 'UnauthorizedProgramHash');
189+
190+
// Initialize an array of uint256 to store the program output
191+
let mut program_output: Array<felt252> = array![];
192+
193+
// Assign values to the program output array
194+
// This needs to be compatible with cairo program
195+
// https://github.com/HerodotusDev/hdp-cairo/blob/main/src/utils/utils.cairo#L27-L48
196+
program_output.append(task_data.task_hash_low.into());
197+
program_output.append(task_data.task_hash_high.into());
198+
program_output.append(task_data.task_result_low.into());
199+
program_output.append(task_data.task_result_high.into());
200+
201+
let state = get_dep_component!(@self, State);
202+
for mmr in task_data.mmr_data {
203+
let mmr_root = state.mmrs.entry(*mmr.chain_id).entry(*mmr.mmr_id).entry(POSEIDON_HASHING_FUNCTION).mmr_size_to_root.entry(*mmr.mmr_size).read();
204+
assert(mmr_root != 0, 'InvalidMmrRoot');
205+
program_output.append((*mmr.mmr_id).try_into().expect('mmr_id not felt252'));
206+
program_output.append((*mmr.mmr_size).try_into().expect('mmr_size not felt252'));
207+
program_output.append((*mmr.chain_id).try_into().expect('chain_id not felt252'));
208+
program_output.append(mmr_root.try_into().expect('mmr_root not felt252'));
209+
};
210+
211+
let fact_hash = calculate_fact_hash(task_data.program_hash, program_output.span());
212+
213+
let cairo_fact_registry = get_dep_component!(@self, CairoFactRegistry);
214+
assert(cairo_fact_registry.isCairoFactValidForInternal(fact_hash), 'Invalid fact');
215+
216+
let task_result = u256 {low: task_data.task_result_low, high: task_data.task_result_high};
217+
218+
// Store the task result
219+
self.cachedTasksResult.entry(task_hash).write(TaskResult {status: TaskStatus::FINALIZED, result: task_result});
220+
self.emit(Event::TaskFinalized(TaskFinalized {task_hash, task_result}));
221+
}
222+
223+
fn getDataProcessorTaskStatus(self: @ComponentState<TContractState>, task_commitment: u256) -> TaskStatus {
224+
self.cachedTasksResult.entry(task_commitment).status.read()
225+
}
226+
227+
fn getDataProcessorFinalizedTaskResult(self: @ComponentState<TContractState>, task_commitment: u256) -> u256 {
228+
let task_result = self.cachedTasksResult.entry(task_commitment).read();
229+
230+
// Ensure task is finalized
231+
assert(task_result.status == TaskStatus::FINALIZED, 'TASK_NOT_FINALIZED');
232+
233+
task_result.result
234+
}
235+
}
236+
}
237+

cairo/src/lib.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod receiver;
55
pub mod evm_growing;
66
pub mod utils;
77
pub mod cairo_fact_registry;
8+
pub mod data_processor;
89
// Main contract is in receiver.cairo file
910
// because L1 handlers cannot be defined in components
1011

cairo/src/receiver.cairo

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod StorageProofs {
1313
evm_fact_registry::evm_fact_registry_component,
1414
mmr_core::{mmr_core_component, RootForHashingFunction}, state::state_component,
1515
evm_growing::evm_growing_component, cairo_fact_registry::cairo_fact_registry_component,
16+
data_processor::data_processor_component,
1617
};
1718
use starknet::{ClassHash, ContractAddress};
1819
use super::*;
@@ -28,6 +29,11 @@ pub mod StorageProofs {
2829
storage: cairo_fact_registry,
2930
event: CairoFactRegistryEvent,
3031
);
32+
component!(
33+
path: data_processor_component,
34+
storage: data_processor,
35+
event: DataProcessorEvent,
36+
);
3137

3238
// Ownable / Upgradeable
3339
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
@@ -50,6 +56,8 @@ pub mod StorageProofs {
5056
evm_growing: evm_growing_component::Storage,
5157
#[substorage(v0)]
5258
cairo_fact_registry: cairo_fact_registry_component::Storage,
59+
#[substorage(v0)]
60+
data_processor: data_processor_component::Storage,
5361
}
5462

5563
#[constructor]
@@ -121,6 +129,8 @@ pub mod StorageProofs {
121129
EvmGrowingEvent: evm_growing_component::Event,
122130
#[flat]
123131
CairoFactRegistryEvent: cairo_fact_registry_component::Event,
132+
#[flat]
133+
DataProcessorEvent: data_processor_component::Event,
124134
}
125135

126136
#[abi(embed_v0)]
@@ -136,6 +146,12 @@ pub mod StorageProofs {
136146
#[abi(embed_v0)]
137147
impl EvmGrowingImpl = evm_growing_component::EvmGrowing<ContractState>;
138148

149+
#[abi(embed_v0)]
150+
impl CairoFactRegistryImpl = cairo_fact_registry_component::CairoFactRegistry<ContractState>;
151+
152+
#[abi(embed_v0)]
153+
impl DataProcessorImpl = data_processor_component::DataProcessor<ContractState>;
154+
139155
// Ownable / Upgradeable
140156
#[abi(embed_v0)]
141157
impl OwnableMixinImpl = OwnableComponent::OwnableCamelOnlyImpl<ContractState>;

0 commit comments

Comments
 (0)