1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
use crate::prelude::*;
pub use hdk_derive::hdk_entry_defs;
pub use hdk_derive::hdk_entry_helper;
#[cfg(doc)]
pub mod examples;
/// MUST get an EntryHashed at a given EntryHash.
///
/// The EntryHashed is NOT guaranteed to be associated with a valid (or even validated) Action/Record.
/// For example, an invalid Record could be published and `must_get_entry` would return the EntryHashed.
///
/// This may be useful during validation callbacks where the validity and relevance of some content can be
/// asserted by the CURRENT validation callback independent of a Record. This behaviour avoids the potential for
/// eclipse attacks to lie about the validity of some data and cause problems for a hApp.
/// If you NEED to know that a dependency is valid in order for the current validation logic
/// (e.g. inductive validation of a tree) then `must_get_valid_record` is likely what you need.
///
/// `must_get_entry` is available in contexts such as validation where both determinism and network access is desirable.
///
/// An EntryHashed will NOT be returned if:
/// - @TODO It is PURGED (community redacted entry)
/// - @TODO ALL actions pointing to it are WITHDRAWN by the authors
/// - ALL actions pointing to it are ABANDONED by ALL authorities due to validation failure
/// - Nobody knows about it on the currently visible network
///
/// If an EntryHashed fails to be returned:
///
/// - Callbacks will return early with `UnresolvedDependencies`
/// - Zome calls will receive a `WasmError` from the host
pub fn must_get_entry(entry_hash: EntryHash) -> ExternResult<EntryHashed> {
HDI.with(|h| {
h.borrow()
.must_get_entry(MustGetEntryInput::new(entry_hash))
})
}
/// MUST get a SignedActionHashed at a given ActionHash.
///
/// The SignedActionHashed is NOT guaranteed to be a valid (or even validated) Record.
/// For example, an invalid Action could be published and `must_get_action` would return the `SignedActionHashed`.
///
/// This may be useful during validation callbacks where the validity depends on an action existing regardless of its associated Entry.
/// For example, we may simply need to check that the author is the same for two referenced Actions.
///
/// `must_get_action` is available in contexts such as validation where both determinism and network access is desirable.
///
/// A `SignedActionHashed` will NOT be returned if:
///
/// - @TODO The action is WITHDRAWN by the author
/// - @TODO The action is ABANDONED by ALL authorities
/// - Nobody knows about it on the currently visible network
///
/// If a `SignedActionHashed` fails to be returned:
///
/// - Callbacks will return early with `UnresolvedDependencies`
/// - Zome calls will receive a `WasmError` from the host
pub fn must_get_action(action_hash: ActionHash) -> ExternResult<SignedActionHashed> {
HDI.with(|h| {
h.borrow()
.must_get_action(MustGetActionInput::new(action_hash))
})
}
/// MUST get a VALID Record at a given ActionHash.
///
/// The Record is guaranteed to be valid.
/// More accurately the Record is guarantee to be consistently reported as valid by the visible network.
///
/// The validity requirement makes this more complex but notably enables inductive validation of arbitrary graph structures.
/// For example "If this Record is valid, and its parent is valid, up to the root, then the whole tree of Records is valid".
///
/// If at least one authority (1 of N trust) claims the Record is invalid then a conflict resolution/warranting round will be triggered.
///
/// In the case of a total eclipse (every visible authority is lying) then we cannot immediately detect an invalid Record.
/// Unlike `must_get_entry` and `must_get_action` we cannot simply inspect the cryptographic integrity to know this.
///
/// In theory we can run validation of the returned Record ourselves, which itself may be based on `must_get_X` calls.
/// If there is a large nested graph of `must_get_valid_record` calls this could be extremely heavy.
/// Note though that each "hop" in recursive validation is routed to a completely different set of authorities.
/// It does not take many hops to reach the point where an attacker needs to eclipse the entire network to lie about Record validity.
///
/// @TODO We keep signed receipts from authorities serving up "valid records".
/// - If we ever discover a record we were told is valid is invalid we can retroactively look to warrant authorities
/// - We can async (e.g. in a background task) be recursively validating Record dependencies ourselves, following hops until there is no room for lies
/// - We can with small probability recursively validate to several hops inline to discourage potential eclipse attacks with a credible immediate threat
///
/// If you do not care about validity and simply want a pair of Action+Entry data, then use both `must_get_action` and `must_get_entry` together.
///
/// `must_get_valid_record` is available in contexts such as validation where both determinism and network access is desirable.
///
/// An `Record` will not be returned if:
///
/// - @TODO It is WITHDRAWN by the author
/// - @TODO The Entry is PURGED by the community
/// - It is ABANDONED by ALL authorities due to failed validation
/// - If ANY authority (1 of N trust) OR ourselves (0 of N trust) believes it INVALID
/// - Nobody knows about it on the visible network
///
/// If an `Record` fails to be returned:
///
/// - Callbacks will return early with `UnresolvedDependencies`
/// - Zome calls will receive a `WasmError` from the host
pub fn must_get_valid_record(action_hash: ActionHash) -> ExternResult<Record> {
HDI.with(|h| {
h.borrow()
.must_get_valid_record(MustGetValidRecordInput::new(action_hash))
})
}
/// Implements conversion traits to allow a struct to be handled as an app entry.
/// If you have some need to implement custom serialization logic or metadata injection
/// you can do so by implementing these traits manually instead.
///
/// This requires that TryFrom and TryInto [`derive@SerializedBytes`] is implemented for the entry type,
/// which implies that [`serde::Serialize`] and [`serde::Deserialize`] is also implemented.
/// These can all be derived and there is an attribute macro that both does the default defines.
#[macro_export]
macro_rules! app_entry {
( $t:ident ) => {
impl TryFrom<&$crate::prelude::Entry> for $t {
type Error = $crate::prelude::WasmError;
fn try_from(entry: &$crate::prelude::Entry) -> Result<Self, Self::Error> {
match entry {
$crate::prelude::Entry::App(eb) => Ok(Self::try_from(
$crate::prelude::SerializedBytes::from(eb.to_owned()),
).map_err(|e| $crate::prelude::wasm_error!(e))?),
$crate::prelude::Entry::CounterSign(_, eb) => Ok(Self::try_from(
$crate::prelude::SerializedBytes::from(eb.to_owned()),
).map_err(|e| $crate::prelude::wasm_error!(e))?),
_ => Err($crate::prelude::wasm_error!(
"{:?} is not an Entry::App or Entry::CounterSign so has no serialized bytes",
entry
)),
}
}
}
impl TryFrom<$crate::prelude::Entry> for $t {
type Error = $crate::prelude::WasmError;
fn try_from(entry: $crate::prelude::Entry) -> Result<Self, Self::Error> {
Self::try_from(&entry)
}
}
impl TryFrom<$crate::prelude::EntryHashed> for $t {
type Error = $crate::prelude::WasmError;
fn try_from(entry_hashed: $crate::prelude::EntryHashed) -> Result<Self, Self::Error> {
Self::try_from(entry_hashed.as_content())
}
}
impl TryFrom<&$crate::prelude::Record> for $t {
type Error = $crate::prelude::WasmError;
fn try_from(record: &$crate::prelude::Record) -> Result<Self, Self::Error> {
Ok(match &record.entry {
RecordEntry::Present(entry) => Self::try_from(entry)?,
_ => return Err(
$crate::prelude::wasm_error!(
$crate::prelude::WasmErrorInner::Guest(format!("Tried to deserialize a record, expecting it to contain entry data, but there was none. Record ActionHash: {}", record.signed_action.hashed.hash))),
)
})
}
}
impl TryFrom<$crate::prelude::Record> for $t {
type Error = $crate::prelude::WasmError;
fn try_from(record: $crate::prelude::Record) -> Result<Self, Self::Error> {
(&record).try_into()
}
}
impl TryFrom<&$t> for $crate::prelude::AppEntryBytes {
type Error = $crate::prelude::WasmError;
fn try_from(t: &$t) -> Result<Self, Self::Error> {
AppEntryBytes::try_from(SerializedBytes::try_from(t).map_err(|e| wasm_error!(e))?).map_err(|entry_error| match entry_error {
EntryError::SerializedBytes(serialized_bytes_error) => {
wasm_error!(WasmErrorInner::Serialize(serialized_bytes_error))
}
EntryError::EntryTooLarge(_) => {
wasm_error!(WasmErrorInner::Guest(entry_error.to_string()))
}
})
}
}
impl TryFrom<$t> for $crate::prelude::AppEntryBytes {
type Error = $crate::prelude::WasmError;
fn try_from(t: $t) -> Result<Self, Self::Error> {
Self::try_from(&t)
}
}
impl TryFrom<&$t> for $crate::prelude::Entry {
type Error = $crate::prelude::WasmError;
fn try_from(t: &$t) -> Result<Self, Self::Error> {
Ok(Self::App($crate::prelude::AppEntryBytes::try_from(t)?))
}
}
impl TryFrom<$t> for $crate::prelude::Entry {
type Error = $crate::prelude::WasmError;
fn try_from(t: $t) -> Result<Self, Self::Error> {
Self::try_from(&t)
}
}
};
}
/// Shorthand to implement the entry defs callback similar to the vec![ .. ] macro but for entries.
///
/// e.g. the following are the same
///
/// ```ignore
/// entry_defs![ Foo::entry_def() ];
/// ```
///
/// ```ignore
/// #[hdk_extern]
/// fn entry_defs(_: ()) -> ExternResult<EntryDefsCallbackResult> {
/// Ok(vec![ Foo::entry_def() ].into())
/// }
/// ```
#[doc(hidden)]
#[macro_export]
macro_rules! entry_defs {
[ $( $def:expr ),* ] => {
#[hdk_extern]
pub fn entry_defs(_: ()) -> $crate::prelude::ExternResult<$crate::prelude::EntryDefsCallbackResult> {
Ok($crate::prelude::EntryDefsCallbackResult::from(vec![ $( $def ),* ]))
}
};
}