kore_contract_sdk/
lib.rs

1// Copyright 2025 Kore Ledger
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4mod error;
5mod externf;
6mod value_wrapper;
7use borsh::{BorshDeserialize, BorshSerialize};
8use error::Error;
9use serde::{Deserialize, Serialize};
10
11pub use self::value_wrapper::ValueWrapper;
12
13/// Contrat execution context.
14#[derive(Serialize, Deserialize, Debug)]
15pub struct Context<Event> {
16    /// Event that triggered the contract execution
17    pub event: Event,
18    /// Is the sender of the event the owner of the contract
19    pub is_owner: bool,
20}
21
22/// Contract execution result.
23#[derive(Serialize, Deserialize, Debug)]
24pub struct ContractResult<State> {
25    /// Final state of the contract.
26    pub state: State,
27    /// Is the contract execution successful?
28    pub success: bool,
29    /// Contract error
30    pub error: String
31}
32
33/// Contract init result.
34#[derive(Serialize, Deserialize, Debug, Default)]
35pub struct ContractInitCheck {
36    /// Is the contract init successful?
37    pub success: bool,
38    /// Contract error
39    pub error: String,
40}
41
42/// Internal contract execution result used for borsh serialization.
43#[derive(BorshSerialize)]
44struct ContractResultBorsh {
45    /// Final state of the contract.
46    pub final_state: ValueWrapper,
47    /// Is the contract execution successful?
48    pub success: bool,
49    /// Contract error
50    pub error: String
51}
52
53/// Internal contract execution result implementation for errors.
54impl ContractResultBorsh {
55    pub fn error(error: &str) -> Self {
56        Self {
57            final_state: ValueWrapper(serde_json::Value::Null),
58            success: false,
59            error: error.to_owned()
60        }
61    }
62}
63
64/// Internal contract execution result used for borsh serialization.
65#[derive(BorshSerialize)]
66struct ContractInitCheckBorsh {
67    /// Is the contract execution successful?
68    pub success: bool,
69    /// Contract error
70    pub error: String
71}
72
73/// Internal contract execution result implementation for errors.
74impl ContractInitCheckBorsh {
75    pub fn error(error: &str) -> Self {
76        Self {
77            success: false,
78            error: error.to_owned()
79        }
80    }
81
82    pub fn ok() -> Self {
83        Self {
84            success: true,
85            error: String::default()
86        }
87    }
88}
89
90/// Internal contract execution result implementation for building results.
91impl<State> ContractResult<State> {
92    pub fn new(state: State) -> Self {
93        Self {
94            state,
95            success: false,
96            error: String::default()
97        }
98    }
99}
100
101/// Contract execution.
102///
103/// # Arguments
104///
105/// * `state_ptr` - Pointer to the initial state of the contract.
106/// * `callback` - Callback that will be executed with the init contract logic.
107///
108/// # Returns
109///
110/// * `result_ptr` - Pointer to the init contract execution result.
111///
112pub fn check_init_data<State, F>(
113    state_ptr: i32,
114    callback: F,
115) -> u32 
116where
117    State: for<'a> Deserialize<'a> + Serialize + Clone,
118    F: Fn(&State, &mut ContractInitCheck),
119{
120    {
121        let error: String;
122        'process: {
123            let Ok(state_value) = deserialize(get_from_context(state_ptr)) else {
124                error = "Can not deserialize State".to_owned();
125                break 'process;
126            };
127            let Ok(state) = serde_json::from_value::<State>(state_value.0) else {
128                error = "Can not convert State from value".to_owned();
129                break 'process;
130            };
131            let mut contract_result = ContractInitCheck::default();
132            callback(&state, &mut contract_result);
133
134            if !contract_result.success {
135                error = format!("Error running init contract data: {}", contract_result.error);
136                break 'process;
137            }
138
139            let Ok(result_ptr) = store(&ContractInitCheckBorsh::ok()) else {
140                error = "Can not return init contract result".to_owned();
141                break 'process;
142            };
143            return result_ptr;
144        }
145        store(&ContractInitCheckBorsh::error(&error)).expect("Contract store process failed")
146    }
147}
148
149/// Contract execution.
150///
151/// # Arguments
152///
153/// * `state_ptr` - Pointer to the initial state of the contract.
154/// * `event_ptr` - Pointer to the event that triggered the contract execution.
155/// * `is_owner` - Is the sender of the event the owner of the contract?
156/// * `callback` - Callback that will be executed with the contract logic.
157///
158/// # Returns
159///
160/// * `result_ptr` - Pointer to the contract execution result.
161///
162pub fn execute_contract<F, State, Event>(
163    state_ptr: i32,
164    init_state_ptr: i32,
165    event_ptr: i32,
166    is_owner: i32,
167    callback: F,
168) -> u32
169where
170    State: for<'a> Deserialize<'a> + Serialize + Clone,
171    Event: for<'a> Deserialize<'a> + Serialize,
172    F: Fn(&Context<Event>, &mut ContractResult<State>),
173{
174    {
175        let error: String;
176        'process: {
177            let Ok(state_value) = deserialize(get_from_context(state_ptr)) else {
178                error = "Can not deserialize State".to_owned();
179                break 'process;
180            };
181            let state = match serde_json::from_value::<State>(state_value.0) {
182                Ok(state) => state,
183                Err(_) => {
184                    let Ok(init_state) = deserialize(get_from_context(init_state_ptr)) else {
185                        error = "Can not deserialize Init State".to_owned();
186                        break 'process;
187                    };
188
189                    let Ok(init_state) = serde_json::from_value::<State>(init_state.0) else {
190                        error = "Can not convert State from value".to_owned();
191                        break 'process;
192                    };
193
194                    init_state
195                }
196            };
197            let Ok(event_value) = deserialize(get_from_context(event_ptr)) else {
198                error = "Can not deserialize Event".to_owned();
199                break 'process;
200            };
201            let Ok(event) = serde_json::from_value::<Event>(event_value.0) else {
202                error = "Can not convert Event from value".to_owned();
203                break 'process;
204            };
205            let is_owner = if is_owner == 1 { true } else { false };
206            let context = Context {
207                event,
208                is_owner
209            };
210            let mut contract_result = ContractResult::new(state);
211            callback(&context, &mut contract_result);
212            let Ok(state_value) = serde_json::to_value(&contract_result.state) else {
213                error = "Can not convert contract final state into Value".to_owned();
214                break 'process;
215            };
216            let result = ContractResultBorsh {
217                final_state: ValueWrapper(state_value),
218                success: contract_result.success,
219                error: format!("Error running contract event: {}", contract_result.error)
220            };
221            // Después de haber sido modificado debemos guardar el nuevo estado.
222            // Sería interesante no tener que guardar estado si el evento no es modificante
223            let Ok(result_ptr) = store(&result) else {
224                error = "Can not return contract result".to_owned();
225                break 'process;
226            };
227            return result_ptr;
228        };
229        store(&ContractResultBorsh::error(&error)).expect("Contract store process failed")
230    }
231}
232
233fn deserialize(bytes: Vec<u8>) -> Result<ValueWrapper, Error> {
234    BorshDeserialize::try_from_slice(&bytes).map_err(|e| Error::Deserialization(e.to_string()))
235}
236
237fn serialize<S: BorshSerialize>(data: S) -> Result<Vec<u8>, Error> {
238    borsh::to_vec(&data).map_err(|e| Error::Serialization(e.to_string()))
239}
240
241fn get_from_context(pointer: i32) -> Vec<u8> {
242    let data = unsafe {
243        let len = externf::pointer_len(pointer);
244        let mut data = vec![];
245        for i in 0..len {
246            data.push(externf::read_byte(pointer + i));
247        }
248        data
249    };
250    data
251}
252
253fn store<S>(data: &S) -> Result<u32, Error>
254where 
255    S: BorshSerialize
256{
257    let bytes = serialize(&data).map_err(|e| Error::Serialization(e.to_string()))?;
258    unsafe {
259        let ptr = externf::alloc(bytes.len() as u32) as u32;
260        for (index, byte) in bytes.into_iter().enumerate() {
261            externf::write_byte(ptr, index as u32, byte);
262        }
263        Ok(ptr)
264    }
265}
266
267#[cfg(test)]
268mod tests {
269}