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