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