sabre_sdk/
lib.rs

1// Copyright 2018 Cargill Incorporated
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(clippy::missing_safety_doc, renamed_and_removed_lints)]
16
17mod externs;
18pub mod log;
19pub mod protocol;
20pub mod protos;
21
22use std::collections::HashMap;
23use std::string::FromUtf8Error;
24
25pub use crate::externs::{WasmPtr, WasmPtrList};
26
27pub struct Header {
28    signer: String,
29}
30
31impl Header {
32    pub fn new(signer: String) -> Header {
33        Header { signer }
34    }
35
36    pub fn get_signer_public_key(&self) -> &str {
37        &self.signer
38    }
39}
40
41pub struct TpProcessRequest<'a> {
42    payload: Vec<u8>,
43    header: &'a mut Header,
44    signature: String,
45}
46
47impl<'a> TpProcessRequest<'a> {
48    pub fn new(payload: Vec<u8>, header: &'a mut Header, signature: String) -> TpProcessRequest {
49        TpProcessRequest {
50            payload,
51            header,
52            signature,
53        }
54    }
55
56    pub fn get_payload(&self) -> &[u8] {
57        &self.payload
58    }
59
60    pub fn get_header(&self) -> &Header {
61        self.header
62    }
63
64    pub fn get_signature(&self) -> String {
65        self.signature.to_string()
66    }
67}
68
69pub trait TransactionContext {
70    #[deprecated(
71        since = "0.2.0",
72        note = "please use `get_state_entry` or `get_state_entries` instead"
73    )]
74    /// get_state queries the validator state for data at each of the
75    /// addresses in the given list. The addresses that have been set
76    /// are returned. get_state is deprecated, please use get_state_entry or get_state_entries
77    /// instead
78    ///
79    /// # Arguments
80    ///
81    /// * `addresses` - the addresses to fetch
82    fn get_state(&self, addresses: &[String]) -> Result<Vec<(String, Vec<u8>)>, WasmSdkError> {
83        self.get_state_entries(addresses)
84    }
85    /// get_state_entry queries the validator state for data at the
86    /// address given. If the address is set, the data is returned.
87    ///
88    /// # Arguments
89    ///
90    /// * `address` - the address to fetch
91    fn get_state_entry(&self, address: &str) -> Result<Option<Vec<u8>>, WasmSdkError> {
92        Ok(self
93            .get_state_entries(&[address.to_string()])?
94            .into_iter()
95            .map(|(_, val)| val)
96            .next())
97    }
98
99    /// get_state_entries queries the validator state for data at each of the
100    /// addresses in the given list. The addresses that have been set
101    /// are returned.
102    ///
103    /// # Arguments
104    ///
105    /// * `addresses` - the addresses to fetch
106    fn get_state_entries(
107        &self,
108        addresses: &[String],
109    ) -> Result<Vec<(String, Vec<u8>)>, WasmSdkError>;
110
111    #[deprecated(
112        since = "0.2.0",
113        note = "please use `set_state_entry` or `set_state_entries` instead"
114    )]
115    /// set_state requests that each address in the provided map be
116    /// set in validator state to its corresponding value. set_state is deprecated, please use
117    /// set_state_entry to set_state_entries instead
118    ///
119    /// # Arguments
120    ///
121    /// * `entries` - entries are a hashmap where the key is an address and value is the data
122    fn set_state(&self, entries: HashMap<String, Vec<u8>>) -> Result<(), WasmSdkError> {
123        let state_entries: Vec<(String, Vec<u8>)> = entries.into_iter().collect();
124        self.set_state_entries(state_entries)
125    }
126
127    /// set_state_entry requests that the provided address is set in the validator state to its
128    /// corresponding value.
129    ///
130    /// # Arguments
131    ///
132    /// * `address` - address of where to store the data
133    /// * `data` - payload is the data to store at the address
134    fn set_state_entry(&self, address: String, data: Vec<u8>) -> Result<(), WasmSdkError> {
135        self.set_state_entries(vec![(address, data)])
136    }
137
138    /// set_state_entries requests that each address in the provided map be
139    /// set in validator state to its corresponding value.
140    ///
141    /// # Arguments
142    ///
143    /// * `entries` - entries are a hashmap where the key is an address and value is the data
144    fn set_state_entries(&self, entries: Vec<(String, Vec<u8>)>) -> Result<(), WasmSdkError>;
145
146    /// delete_state requests that each of the provided addresses be unset
147    /// in validator state. A list of successfully deleted addresses is returned.
148    /// delete_state is deprecated, please use delete_state_entry to delete_state_entries instead
149    ///
150    /// # Arguments
151    ///
152    /// * `addresses` - the addresses to delete
153    #[deprecated(
154        since = "0.2.0",
155        note = "please use `delete_state_entry` or `delete_state_entries` instead"
156    )]
157    fn delete_state(&self, addresses: &[String]) -> Result<Vec<String>, WasmSdkError> {
158        self.delete_state_entries(addresses)
159    }
160
161    /// delete_state_entry requests that the provided address be unset
162    /// in validator state. A list of successfully deleted addresses
163    /// is returned.
164    ///
165    /// # Arguments
166    ///
167    /// * `address` - the address to delete
168    fn delete_state_entry(&self, address: &str) -> Result<Option<String>, WasmSdkError> {
169        Ok(self
170            .delete_state_entries(&[address.to_string()])?
171            .into_iter()
172            .next())
173    }
174
175    /// delete_state_entries requests that each of the provided addresses be unset
176    /// in validator state. A list of successfully deleted addresses
177    /// is returned.
178    ///
179    /// # Arguments
180    ///
181    /// * `addresses` - the addresses to delete
182    fn delete_state_entries(&self, addresses: &[String]) -> Result<Vec<String>, WasmSdkError>;
183
184    /// add_event adds a new event to the execution result for this transaction.
185    ///
186    /// # Arguments
187    ///
188    /// * `event_type` -  This is used to subscribe to events. It should be globally unique and
189    ///          describe what, in general, has occured.
190    /// * `attributes` - Additional information about the event that is transparent to the
191    ///          validator. Attributes can be used by subscribers to filter the type of events
192    ///          they receive.
193    /// * `data` - Additional information about the event that is opaque to the validator.
194    fn add_event(
195        &self,
196        event_type: String,
197        attributes: Vec<(String, String)>,
198        data: &[u8],
199    ) -> Result<(), WasmSdkError>;
200}
201
202#[derive(Default)]
203pub struct SabreTransactionContext {}
204
205impl SabreTransactionContext {
206    pub fn new() -> SabreTransactionContext {
207        SabreTransactionContext {}
208    }
209}
210
211impl TransactionContext for SabreTransactionContext {
212    fn get_state_entries(
213        &self,
214        addresses: &[String],
215    ) -> Result<Vec<(String, Vec<u8>)>, WasmSdkError> {
216        unsafe {
217            if addresses.is_empty() {
218                return Err(WasmSdkError::InvalidTransaction("No address to get".into()));
219            }
220            let head = &addresses[0];
221            let header_address_buffer = WasmBuffer::new(head.as_bytes())?;
222            externs::create_collection(header_address_buffer.to_raw());
223
224            for addr in addresses[1..].iter() {
225                let wasm_buffer = WasmBuffer::new(addr.as_bytes())?;
226                externs::add_to_collection(header_address_buffer.to_raw(), wasm_buffer.to_raw());
227            }
228
229            let results =
230                WasmBuffer::from_list(externs::get_state(header_address_buffer.to_raw()))?;
231            let mut result_vec = Vec::new();
232
233            if (result_vec.len() % 2) != 0 {
234                return Err(WasmSdkError::InvalidTransaction(
235                    "Get state returned incorrect data fmt".into(),
236                ));
237            }
238
239            for result in results.chunks(2) {
240                let addr = String::from_utf8(result[0].to_bytes())?;
241                result_vec.push((addr, result[1].to_bytes()))
242            }
243            Ok(result_vec)
244        }
245    }
246
247    fn set_state_entries(&self, entries: Vec<(String, Vec<u8>)>) -> Result<(), WasmSdkError> {
248        unsafe {
249            let mut entries_iter = entries.iter();
250            let (head, head_data) = match entries_iter.next() {
251                Some((addr, data)) => (addr, data),
252                None => return Err(WasmSdkError::InvalidTransaction("No entries to set".into())),
253            };
254
255            let header_address_buffer = WasmBuffer::new(head.as_bytes())?;
256            externs::create_collection(header_address_buffer.to_raw());
257
258            let wasm_head_data_buffer = WasmBuffer::new(head_data)?;
259            externs::add_to_collection(
260                header_address_buffer.to_raw(),
261                wasm_head_data_buffer.to_raw(),
262            );
263
264            for (address, data) in entries_iter {
265                let wasm_addr_buffer = WasmBuffer::new(address.as_bytes())?;
266                externs::add_to_collection(
267                    header_address_buffer.to_raw(),
268                    wasm_addr_buffer.to_raw(),
269                );
270
271                let wasm_data_buffer = WasmBuffer::new(data)?;
272                externs::add_to_collection(
273                    header_address_buffer.to_raw(),
274                    wasm_data_buffer.to_raw(),
275                );
276            }
277
278            let result = externs::set_state(header_address_buffer.to_raw());
279
280            if result == 0 {
281                return Err(WasmSdkError::InvalidTransaction(
282                    "Unable to set state".into(),
283                ));
284            }
285        }
286        Ok(())
287    }
288
289    fn delete_state_entries(&self, addresses: &[String]) -> Result<Vec<String>, WasmSdkError> {
290        unsafe {
291            if addresses.is_empty() {
292                return Err(WasmSdkError::InvalidTransaction(
293                    "No address to delete".into(),
294                ));
295            }
296            let head = &addresses[0];
297            let header_address_buffer = WasmBuffer::new(head.as_bytes())?;
298            externs::create_collection(header_address_buffer.to_raw());
299
300            for addr in addresses[1..].iter() {
301                let wasm_buffer = WasmBuffer::new(addr.as_bytes())?;
302                externs::add_to_collection(header_address_buffer.to_raw(), wasm_buffer.to_raw());
303            }
304            let result =
305                WasmBuffer::from_list(externs::delete_state(header_address_buffer.to_raw()))?;
306            let mut result_vec = Vec::new();
307            for i in result {
308                let addr = String::from_utf8(i.data)?;
309                result_vec.push(addr);
310            }
311            Ok(result_vec)
312        }
313    }
314
315    fn add_event(
316        &self,
317        event_type: String,
318        attributes: Vec<(String, String)>,
319        data: &[u8],
320    ) -> Result<(), WasmSdkError> {
321        unsafe {
322            // Get the WasmBuffer of event_type
323            let event_type_buffer = WasmBuffer::new(event_type.as_bytes())?;
324
325            // Get the WasmBuffer of data
326            let data_buffer = WasmBuffer::new(data)?;
327
328            // Get attributes tuple stored in a collection
329            // List starts with a dummy entry "attributes", this is to allow empty
330            // attributes list from the SDK
331            // Entry at odd index: Key
332            // Entry at even index: Value
333            let attributes_iter = attributes.iter();
334            let attributes_buffer = WasmBuffer::new(b"attributes")?;
335            externs::create_collection(attributes_buffer.to_raw());
336
337            for (key, value) in attributes_iter {
338                let key_buffer = WasmBuffer::new(key.as_bytes())?;
339                externs::add_to_collection(attributes_buffer.to_raw(), key_buffer.to_raw());
340                let value_buffer = WasmBuffer::new(value.as_bytes())?;
341                externs::add_to_collection(attributes_buffer.to_raw(), value_buffer.to_raw());
342            }
343
344            let result = externs::add_event(
345                event_type_buffer.to_raw(),
346                attributes_buffer.to_raw(),
347                data_buffer.to_raw(),
348            );
349
350            if result != 0 {
351                return Err(WasmSdkError::InvalidTransaction(
352                    "Unable to add event".into(),
353                ));
354            }
355
356            Ok(())
357        }
358    }
359}
360
361// Mimics the sawtooth sdk TransactionHandler
362pub trait TransactionHandler {
363    fn family_name(&self) -> String;
364    fn family_versions(&self) -> Vec<String>;
365    fn namespaces(&self) -> Vec<String>;
366    fn apply(
367        &self,
368        request: &TpProcessRequest,
369        context: &mut dyn TransactionContext,
370    ) -> Result<(), ApplyError>;
371}
372
373/// -1: Failed to deserialize payload
374/// -2: Failed to deserialize signer
375/// -3: apply returned InvalidTransaction
376/// -4: apply returned InternalError
377///
378/// # Safety
379///
380/// This function is unsafe due to the call to WasmBuffer::from_raw which converts a WasmPtr
381/// to a WasmBuffer to access location in executor memory
382pub unsafe fn execute_entrypoint<F>(
383    payload_ptr: WasmPtr,
384    signer_ptr: WasmPtr,
385    signature_ptr: WasmPtr,
386    apply: F,
387) -> i32
388where
389    F: Fn(&TpProcessRequest, &mut dyn TransactionContext) -> Result<bool, ApplyError>,
390{
391    let payload = if let Ok(i) = WasmBuffer::from_raw(payload_ptr) {
392        i.to_bytes()
393    } else {
394        return -1;
395    };
396
397    let signature = if let Ok(i) = WasmBuffer::from_raw(signature_ptr) {
398        match i.to_string() {
399            Ok(s) => s,
400            Err(_) => return -2,
401        }
402    } else {
403        return -1;
404    };
405
406    let signer = if let Ok(i) = WasmBuffer::from_raw(signer_ptr) {
407        match i.to_string() {
408            Ok(s) => s,
409            Err(_) => return -2,
410        }
411    } else {
412        return -1;
413    };
414
415    let mut header = Header::new(signer);
416    match apply(
417        &TpProcessRequest::new(payload, &mut header, signature),
418        &mut SabreTransactionContext::new(),
419    ) {
420        Ok(r) => {
421            if r {
422                1
423            } else {
424                0
425            }
426        }
427        Err(ApplyError::InvalidTransaction(_)) => -3,
428        Err(ApplyError::InternalError(_)) => -4,
429    }
430}
431
432pub struct Request {
433    roles: Vec<String>,
434    org_id: String,
435    public_key: String,
436    payload: Vec<u8>,
437}
438
439impl Request {
440    pub fn new(
441        roles: Vec<String>,
442        org_id: String,
443        public_key: String,
444        payload: Vec<u8>,
445    ) -> Request {
446        Request {
447            roles,
448            org_id,
449            public_key,
450            payload,
451        }
452    }
453
454    pub fn get_roles(&self) -> Vec<String> {
455        self.roles.clone()
456    }
457
458    pub fn get_org_id(&self) -> String {
459        self.org_id.clone()
460    }
461
462    pub fn get_public_key(&self) -> String {
463        self.public_key.clone()
464    }
465
466    pub fn get_state(&self, address: String) -> Result<Option<Vec<u8>>, WasmSdkError> {
467        unsafe {
468            let wasm_buffer = WasmBuffer::new(address.as_bytes())?;
469            ptr_to_vec(externs::get_state(wasm_buffer.to_raw()))
470        }
471    }
472
473    pub fn get_payload<T>(&self) -> Vec<u8> {
474        self.payload.clone()
475    }
476}
477
478/// A WasmBuffer is a wrapper around a wasm pointer.
479///
480/// It contains a raw wasm pointer to location in executor
481/// memory and a bytes repesentation of it's contents.
482///
483/// It offers methods for accessing the data stored at the
484/// location referenced by the raw pointer.
485///
486pub struct WasmBuffer {
487    raw: WasmPtr,
488    data: Vec<u8>,
489}
490
491impl WasmBuffer {
492    pub unsafe fn new(buffer: &[u8]) -> Result<WasmBuffer, WasmSdkError> {
493        let raw = externs::alloc(buffer.len());
494
495        if raw < 0 {
496            return Err(WasmSdkError::AllocError(
497                "Failed to allocate host memory".into(),
498            ));
499        }
500
501        for (i, byte) in buffer.iter().enumerate() {
502            if externs::write_byte(raw, i as u32, *byte) < 0 {
503                return Err(WasmSdkError::MemoryWriteError(
504                    "Failed to write data to host memory".into(),
505                ));
506            }
507        }
508
509        Ok(WasmBuffer {
510            raw,
511            data: buffer.to_vec(),
512        })
513    }
514
515    pub unsafe fn from_raw(raw: WasmPtr) -> Result<WasmBuffer, WasmSdkError> {
516        let data = ptr_to_vec(raw)?.unwrap_or_default();
517        Ok(WasmBuffer { raw, data })
518    }
519
520    pub unsafe fn from_list(ptr: WasmPtrList) -> Result<Vec<WasmBuffer>, WasmSdkError> {
521        let mut wasm_buffers = Vec::new();
522
523        if ptr >= 0 {
524            for i in 0..externs::get_ptr_collection_len(ptr) {
525                let ptr = externs::get_ptr_from_collection(ptr, i as u32);
526
527                if ptr < 0 {
528                    return Err(WasmSdkError::MemoryRetrievalError(
529                        "pointer not found".into(),
530                    ));
531                }
532                wasm_buffers.push(WasmBuffer::from_raw(ptr)?);
533            }
534        }
535
536        Ok(wasm_buffers)
537    }
538
539    pub fn to_bytes(&self) -> Vec<u8> {
540        self.data.clone()
541    }
542
543    pub fn to_raw(&self) -> WasmPtr {
544        self.raw
545    }
546
547    pub fn to_string(&self) -> Result<String, WasmSdkError> {
548        String::from_utf8(self.data.clone()).map_err(WasmSdkError::from)
549    }
550}
551
552#[derive(Debug)]
553pub enum WasmSdkError {
554    InvalidTransaction(String),
555    InternalError(String),
556    StateSetError(String),
557    AllocError(String),
558    MemoryWriteError(String),
559    MemoryRetrievalError(String),
560    Utf8EncodeError(FromUtf8Error),
561    ProtobufError(protobuf::ProtobufError),
562}
563
564impl std::fmt::Display for WasmSdkError {
565    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
566        match *self {
567            WasmSdkError::InvalidTransaction(ref s) => write!(f, "InvalidTransactio: {}", s),
568            WasmSdkError::InternalError(ref s) => write!(f, "InternalError: {}", s),
569            WasmSdkError::StateSetError(ref s) => write!(f, "StateSetError: {}", s),
570            WasmSdkError::AllocError(ref s) => write!(f, "AllocError: {}", s),
571            WasmSdkError::MemoryWriteError(ref s) => write!(f, "MemoryWriteError: {}", s),
572            WasmSdkError::MemoryRetrievalError(ref s) => write!(f, "MemoryRetrievalError: {}", s),
573            WasmSdkError::Utf8EncodeError(ref err) => write!(f, "Utf8EncodeError: {}", err),
574            WasmSdkError::ProtobufError(ref err) => write!(f, "ProtobufError: {}", err),
575        }
576    }
577}
578
579impl From<FromUtf8Error> for WasmSdkError {
580    fn from(e: FromUtf8Error) -> Self {
581        WasmSdkError::Utf8EncodeError(e)
582    }
583}
584
585impl From<protobuf::ProtobufError> for WasmSdkError {
586    fn from(e: protobuf::ProtobufError) -> Self {
587        WasmSdkError::ProtobufError(e)
588    }
589}
590
591#[derive(Debug)]
592pub enum ApplyError {
593    InvalidTransaction(String),
594    InternalError(String),
595}
596
597impl std::fmt::Display for ApplyError {
598    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
599        match *self {
600            ApplyError::InvalidTransaction(ref s) => write!(f, "InvalidTransaction: {}", s),
601            ApplyError::InternalError(ref s) => write!(f, "InternalError: {}", s),
602        }
603    }
604}
605
606impl From<WasmSdkError> for ApplyError {
607    fn from(e: WasmSdkError) -> Self {
608        match e {
609            WasmSdkError::InternalError(..) => ApplyError::InternalError(format!("{}", e)),
610            _ => ApplyError::InvalidTransaction(format!("{}", e)),
611        }
612    }
613}
614
615unsafe fn ptr_to_vec(ptr: WasmPtr) -> Result<Option<Vec<u8>>, WasmSdkError> {
616    let mut vec = Vec::new();
617
618    let ptr_len = externs::get_ptr_len(ptr);
619    if ptr_len == -1 {
620        return Err(WasmSdkError::MemoryRetrievalError(
621            "WasmPtr does not exist".to_string(),
622        ));
623    }
624
625    for i in 0..ptr_len {
626        vec.push(externs::read_byte(ptr as isize + i));
627    }
628
629    if vec.is_empty() {
630        return Ok(None);
631    }
632    Ok(Some(vec))
633}
634
635#[derive(PartialOrd, PartialEq, Copy, Clone)]
636pub enum LogLevel {
637    Trace,
638    Debug,
639    Info,
640    Warn,
641    Error,
642}
643
644pub fn log_message(log_level: LogLevel, log_string: String) {
645    unsafe {
646        // WasmBuffer was created properly, log message otherwise ignore
647        if let Ok(log_buffer) = WasmBuffer::new(log_string.as_bytes()) {
648            match log_level {
649                LogLevel::Trace => externs::log_buffer(4, log_buffer.to_raw()),
650                LogLevel::Debug => externs::log_buffer(3, log_buffer.to_raw()),
651                LogLevel::Info => externs::log_buffer(2, log_buffer.to_raw()),
652                LogLevel::Warn => externs::log_buffer(1, log_buffer.to_raw()),
653                LogLevel::Error => externs::log_buffer(0, log_buffer.to_raw()),
654            };
655        }
656    }
657}
658
659pub fn log_level() -> LogLevel {
660    unsafe {
661        match externs::log_level() {
662            4 => LogLevel::Trace,
663            3 => LogLevel::Debug,
664            2 => LogLevel::Info,
665            1 => LogLevel::Warn,
666            _ => LogLevel::Error,
667        }
668    }
669}
670
671pub fn log_enabled(lvl: LogLevel) -> bool {
672    lvl >= log_level()
673}