Skip to content

Commit

Permalink
Perform raw copy of fixed entries between sdk and kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
fridrik01 committed Jul 12, 2023
1 parent fc91b20 commit cc3263f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 49 deletions.
49 changes: 27 additions & 22 deletions fvm/src/kernel/default.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use byteorder::{BigEndian, ByteOrder};
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::panic::{self, UnwindSafe};
Expand All @@ -18,7 +17,7 @@ use fvm_shared::consensus::ConsensusFault;
use fvm_shared::crypto::signature;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ErrorNumber;
use fvm_shared::event::{ActorEvent, Entry, Flags};
use fvm_shared::event::{ActorEvent, Entry, EntryFixed, Flags};
use fvm_shared::piece::{zero_piece_commitment, PaddedPieceSize};
use fvm_shared::sector::RegisteredPoStProof::{StackedDRGWindow32GiBV1, StackedDRGWindow32GiBV1P1};
use fvm_shared::sector::{RegisteredPoStProof, SectorInfo};
Expand Down Expand Up @@ -1050,49 +1049,55 @@ where

// parse the fixed sized fields from the raw_evt buffer
//
let flags = match Flags::from_bits(BigEndian::read_u64(&view[..8])) {
Some(f) => f,
None => return Err(syscall_error!(IllegalArgument, "invalid flag").into()),
let entry_fixed: EntryFixed = unsafe {
std::mem::transmute::<[u8; BYTES_PER_ENTRY], EntryFixed>(view.try_into().unwrap())
};
let key_len = BigEndian::read_u32(&view[8..12]);
let codec = BigEndian::read_u64(&view[12..20]);
let val_len = BigEndian::read_u32(&view[20..24]);

// make sure that the fixed parsed values are within bounds before we do any allocation
//
if key_len > MAX_KEY_LEN as u32 {
return Err(syscall_error!(IllegalArgument; "event key exceeded max size: {} > {MAX_KEY_LEN}", key_len).into());
let flags = entry_fixed.flags;
if Flags::from_bits(flags.bits()).is_none() {
return Err(
syscall_error!(IllegalArgument; "event flags are invalid: {}", flags.bits())
.into(),
);
}
if val_len > MAX_VALUE_LEN as u32 {
return Err(syscall_error!(IllegalArgument; "event value exceeded max size: {} > {MAX_VALUE_LEN}", val_len).into());
if entry_fixed.key_len > MAX_KEY_LEN as u32 {
return Err(syscall_error!(IllegalArgument; "event key exceeded max size: {} > {MAX_KEY_LEN}", entry_fixed.key_len as u32).into());
}
if codec != IPLD_RAW {
if entry_fixed.val_len > MAX_VALUE_LEN as u32 {
return Err(syscall_error!(IllegalArgument; "event value exceeded max size: {} > {MAX_VALUE_LEN}", entry_fixed.val_len as u32).into());
}
if entry_fixed.codec != IPLD_RAW {
return Err(
syscall_error!(IllegalCodec; "event codec must be IPLD_RAW, was: {}", codec)
syscall_error!(IllegalCodec; "event codec must be IPLD_RAW, was: {}", entry_fixed.codec as u64)
.into(),
);
}

// parse the variable sized fields from the raw_key/raw_val buffers
//
let key = match std::str::from_utf8(&raw_key[key_offset..key_offset + key_len as usize])
{
let key = match std::str::from_utf8(
&raw_key[key_offset..key_offset + entry_fixed.key_len as usize],
) {
Ok(s) => s,
Err(_) => return Err(syscall_error!(IllegalArgument, "event key").into()),
Err(e) => {
return Err(syscall_error!(IllegalArgument, "invalid event key: {}", e).into())
}
};
let value = &raw_val[val_offset..val_offset + val_len as usize];
let value = &raw_val[val_offset..val_offset + entry_fixed.val_len as usize];

// we have all we need to construct a new Entry
let entry = Entry {
flags,
flags: entry_fixed.flags,
key: key.to_string(),
codec,
codec: entry_fixed.codec,
value: value.to_vec(),
};

// shift the key/value offsets
key_offset += key_len as usize;
val_offset += val_len as usize;
key_offset += entry_fixed.key_len as usize;
val_offset += entry_fixed.val_len as usize;

entries.push(entry);
}
Expand Down
48 changes: 21 additions & 27 deletions sdk/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,50 @@
use std::mem::size_of;

// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use crate::{sys, SyscallResult};
use byteorder::{BigEndian, ByteOrder};
use fvm_shared::event::ActorEvent;
use fvm_shared::event::{ActorEvent, EntryFixed};

pub fn emit_event(evt: &ActorEvent) -> SyscallResult<()> {
// we manually serialize the ActorEvent (not using CBOR) into three byte arrays so
// we can accurately charge gas without needing to parse anything inside the FVM
const BYTES_PER_ENTRY: usize = 24;

let mut total_key_len: usize = 0;
let mut total_val_len: usize = 0;

let mut v = vec![0u8; evt.entries.len() * BYTES_PER_ENTRY];
let mut fixed_entries = Vec::with_capacity(evt.entries.len());
for i in 0..evt.entries.len() {
let e = &evt.entries[i];
let offset = i * BYTES_PER_ENTRY;

let view = &mut v[offset..offset + BYTES_PER_ENTRY];
BigEndian::write_u64(&mut view[..8], e.flags.bits());
BigEndian::write_u32(&mut view[8..12], e.key.len() as u32);
BigEndian::write_u64(&mut view[12..20], e.codec);
BigEndian::write_u32(&mut view[20..24], e.value.len() as u32);
fixed_entries.push(EntryFixed {
flags: e.flags,
codec: e.codec,
key_len: e.key.len() as u32,
val_len: e.value.len() as u32,
});

total_key_len += e.key.len();
total_val_len += e.value.len();
}

let mut keys = vec![0u8; total_key_len];
let mut offset: usize = 0;
let mut keys = Vec::with_capacity(total_key_len);
for i in 0..evt.entries.len() {
let e = &evt.entries[i];
keys[offset..offset + e.key.len()].copy_from_slice(e.key.as_bytes());
offset += e.key.len();
keys.extend_from_slice(evt.entries[i].key.as_bytes());
}

let mut values = vec![0u8; total_val_len];
let mut offset: usize = 0;
let mut values = Vec::with_capacity(total_val_len);
for i in 0..evt.entries.len() {
let e = &evt.entries[i];
values[offset..offset + e.value.len()].copy_from_slice(e.value.as_slice());
offset += e.value.len();
values.extend_from_slice(evt.entries[i].value.as_slice());
}

unsafe {
let fixed_size_in_bytes = fixed_entries.len() * size_of::<EntryFixed>();
sys::event::emit_event(
v.as_slice().as_ptr(),
v.as_slice().len() as u32,
keys.as_slice().as_ptr(),
keys.as_slice().len() as u32,
values.as_slice().as_ptr(),
values.as_slice().len() as u32,
fixed_entries.as_ptr() as *const u8,
fixed_size_in_bytes as u32,
keys.as_ptr(),
keys.len() as u32,
values.as_ptr(),
values.len() as u32,
)
}
}
9 changes: 9 additions & 0 deletions shared/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ pub struct Entry {
#[serde(with = "strict_bytes")]
pub value: Vec<u8>,
}

// A fixed sized struct for serializing Entry separately from the key/value bytes.
#[repr(C, packed)]
pub struct EntryFixed {
pub flags: Flags,
pub codec: u64,
pub key_len: u32,
pub val_len: u32,
}

0 comments on commit cc3263f

Please sign in to comment.