use crate::core::ribosome::weigh_placeholder;
use crate::core::ribosome::CallContext;
use crate::core::ribosome::HostFnAccess;
use crate::core::ribosome::RibosomeError;
use crate::core::ribosome::RibosomeT;
use holochain_types::prelude::*;
use holochain_wasmer_host::prelude::*;
use std::sync::Arc;
use wasmer::RuntimeError;
#[allow(clippy::extra_unused_lifetimes)]
pub fn create<'a>(
_ribosome: Arc<impl RibosomeT>,
call_context: Arc<CallContext>,
input: CreateInput,
) -> Result<ActionHash, RuntimeError> {
match HostFnAccess::from(&call_context.host_context()) {
HostFnAccess {
write_workspace: Permission::Allow,
..
} => {
let CreateInput {
entry_location,
entry_visibility,
entry,
chain_top_ordering,
} = input;
let weight = weigh_placeholder();
match entry {
Entry::CounterSign(_, _) => tokio_helper::block_forever_on(async move {
call_context
.host_context
.workspace_write()
.source_chain()
.as_ref()
.expect("Must have source chain if write_workspace access is given")
.put_countersigned(entry, chain_top_ordering, weight)
.await
.map_err(|source_chain_error| -> RuntimeError {
wasm_error!(WasmErrorInner::Host(source_chain_error.to_string())).into()
})
}),
_ => {
let entry_hash = EntryHash::with_data_sync(&entry);
let entry_type = match entry_location {
EntryDefLocation::App(AppEntryDefLocation {
zome_index,
entry_def_index,
}) => {
let app_entry_def =
AppEntryDef::new(entry_def_index, zome_index, entry_visibility);
EntryType::App(app_entry_def)
}
EntryDefLocation::CapGrant => EntryType::CapGrant,
EntryDefLocation::CapClaim => EntryType::CapClaim,
};
let action_builder = builder::Create {
entry_type,
entry_hash,
};
tokio_helper::block_forever_on(async move {
call_context
.host_context
.workspace_write()
.source_chain()
.as_ref()
.expect("Must have source chain if write_workspace access is given")
.put_weightless(action_builder, Some(entry), chain_top_ordering)
.await
.map_err(|source_chain_error| -> RuntimeError {
wasm_error!(WasmErrorInner::Host(source_chain_error.to_string()))
.into()
})
})
}
}
}
_ => Err(wasm_error!(WasmErrorInner::Host(
RibosomeError::HostFnPermissions(
call_context.zome.zome_name().clone(),
call_context.function_name().clone(),
"create".into(),
)
.to_string(),
))
.into()),
}
}
#[cfg(test)]
#[cfg(feature = "slow_tests")]
pub mod wasm_test {
use super::create;
use crate::fixt::*;
use crate::sweettest::*;
use crate::test_utils::RibosomeTestFixture;
use ::fixt::prelude::*;
use hdk::prelude::*;
use holo_hash::AnyDhtHash;
use holo_hash::EntryHash;
use holochain_state::source_chain::SourceChainResult;
use holochain_trace;
use holochain_types::prelude::*;
use holochain_wasm_test_utils::TestWasm;
use holochain_wasm_test_utils::TestWasmPair;
use std::sync::Arc;
#[tokio::test(flavor = "multi_thread")]
async fn create_entry_test() {
let ribosome =
RealRibosomeFixturator::new(crate::fixt::Zomes(vec![TestWasm::Create]))
.next()
.unwrap();
let mut call_context = CallContextFixturator::new(Unpredictable).next().unwrap();
call_context.zome = TestWasmPair::<IntegrityZome, CoordinatorZome>::from(TestWasm::Create)
.coordinator
.erase_type();
let host_access = fixt!(ZomeCallHostAccess, Predictable);
let host_access_2 = host_access.clone();
call_context.host_context = host_access.into();
let app_entry = EntryFixturator::new(AppEntry).next().unwrap();
let input = CreateInput::new(
EntryDefLocation::app(0, 0),
EntryVisibility::Public,
app_entry.clone(),
ChainTopOrdering::default(),
);
let output = create(Arc::new(ribosome), Arc::new(call_context), input).unwrap();
let chain_head = tokio_helper::block_forever_on(async move {
let _ = &host_access_2;
SourceChainResult::Ok(
host_access_2
.workspace
.source_chain()
.as_ref()
.unwrap()
.chain_head()
.unwrap()
.unwrap()
.action,
)
})
.unwrap();
assert_eq!(chain_head, output);
}
#[tokio::test(flavor = "multi_thread")]
async fn ribosome_create_entry_test() {
holochain_trace::test_run();
let RibosomeTestFixture {
conductor, alice, ..
} = RibosomeTestFixture::new(TestWasm::Create).await;
let _output: ActionHash = conductor.call(&alice, "create_entry", ()).await;
let round: Option<Record> = conductor.call(&alice, "get_entry", ()).await;
let round_twice: Vec<Option<Record>> = conductor.call(&alice, "get_entry_twice", ()).await;
let bytes: Vec<u8> = match round.clone().and_then(|el| el.into()) {
Some(holochain_zome_types::entry::Entry::App(entry_bytes)) => {
entry_bytes.bytes().to_vec()
}
other => panic!("unexpected output: {other:?}"),
};
assert_eq!(vec![163, 102, 111, 111], bytes);
assert_eq!(round_twice, vec![round.clone(), round],);
}
#[tokio::test(flavor = "multi_thread")]
async fn multiple_create_entry_limit_test() {
const N: u32 = 50;
holochain_trace::test_run();
let mut conductor = SweetConductor::from_standard_config().await;
let (dna, _, _) = SweetDnaFile::unique_from_test_wasms(vec![TestWasm::MultipleCalls]).await;
let app = conductor.setup_app("app", [&dna]).await.unwrap();
let (cell,) = app.into_tuple();
let _: () = conductor
.call(
&cell.zome(TestWasm::MultipleCalls),
"create_entry_multiple",
N,
)
.await;
let output: holochain_zome_types::bytes::Bytes = conductor
.call(&cell.zome(TestWasm::MultipleCalls), "get_entry_multiple", N)
.await;
let expected: Vec<u8> = (0..N).flat_map(|i| i.to_le_bytes()).collect();
assert_eq!(output.into_vec(), expected);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_serialize_bytes_hash() {
holochain_trace::test_run();
#[derive(Default, SerializedBytes, Serialize, Deserialize, Debug)]
#[repr(transparent)]
#[serde(transparent)]
struct Post(String);
impl TryFrom<&Post> for Entry {
type Error = EntryError;
fn try_from(post: &Post) -> Result<Self, Self::Error> {
Entry::app(post.try_into()?)
}
}
let entry: Entry = (&Post("foo".into())).try_into().unwrap();
let entry_hash = EntryHash::with_data_sync(&entry);
assert_eq!(
"uhCEkPjYXxw4ztKx3wBsxzm-q3Rfoy1bXWbIQohifqC3_HNle3-SO",
&entry_hash.to_string()
);
let sb: SerializedBytes = entry_hash.try_into().unwrap();
let entry_hash: EntryHash = sb.try_into().unwrap();
assert_eq!(
"uhCEkPjYXxw4ztKx3wBsxzm-q3Rfoy1bXWbIQohifqC3_HNle3-SO",
&entry_hash.to_string()
);
let any_hash: AnyDhtHash = entry_hash.clone().into();
assert_eq!(
"uhCEkPjYXxw4ztKx3wBsxzm-q3Rfoy1bXWbIQohifqC3_HNle3-SO",
&entry_hash.to_string()
);
let sb: SerializedBytes = any_hash.try_into().unwrap();
tracing::debug!(any_sb = ?sb);
let any_hash: AnyDhtHash = sb.try_into().unwrap();
assert_eq!(
"uhCEkPjYXxw4ztKx3wBsxzm-q3Rfoy1bXWbIQohifqC3_HNle3-SO",
&any_hash.to_string()
);
let any_hash: AnyDhtHash = entry_hash.clone().into();
assert_eq!(
"uhCEkPjYXxw4ztKx3wBsxzm-q3Rfoy1bXWbIQohifqC3_HNle3-SO",
&any_hash.to_string()
);
}
}