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 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
//! Defines a Record, the basic unit of Holochain data.
use crate::action::conversions::WrongActionError;
use crate::action::ActionHashed;
use crate::action::CreateLink;
use crate::action::DeleteLink;
use crate::entry_def::EntryVisibility;
use crate::signature::Signature;
use crate::Action;
use crate::Entry;
use holo_hash::ActionHash;
use holo_hash::HashableContent;
use holo_hash::HoloHashOf;
use holo_hash::HoloHashed;
use holochain_serialized_bytes::prelude::*;
/// a chain record containing the signed action along with the
/// entry if the action type has one.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Record<A = SignedActionHashed> {
/// The signed action for this record
pub signed_action: A,
/// If there is an entry associated with this action it will be here.
/// If not, there will be an enum variant explaining the reason.
pub entry: RecordEntry,
}
impl<A> AsRef<A> for Record<A> {
fn as_ref(&self) -> &A {
&self.signed_action
}
}
/// Represents the different ways the entry_address reference within an action
/// can be intepreted
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum RecordEntry {
/// The Action has an entry_address reference, and the Entry is accessible.
Present(Entry),
/// The Action has an entry_address reference, but we are in a public
/// context and the entry is private.
Hidden,
/// The Action does not contain an entry_address reference.
NotApplicable,
/// The Action has an entry but was stored without it.
/// This can happen when you receive gossip of just an action
/// when the action type is a [`crate::EntryCreationAction`]
NotStored,
}
/// The hashed action and the signature that signed it
pub type SignedActionHashed = SignedHashed<Action>;
impl AsRef<SignedActionHashed> for SignedActionHashed {
fn as_ref(&self) -> &SignedActionHashed {
self
}
}
#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
/// Any content that has been hashed and signed.
pub struct SignedHashed<T>
where
T: HashableContent,
{
/// The hashed content.
pub hashed: HoloHashed<T>,
/// The signature of the content.
pub signature: Signature,
}
impl Record {
/// Raw record constructor. Used only when we know that the values are valid.
/// NOTE: this will NOT hide private entry data if present!
pub fn new(signed_action: SignedActionHashed, maybe_entry: Option<Entry>) -> Self {
let maybe_visibility = signed_action
.action()
.entry_data()
.map(|(_, entry_type)| entry_type.visibility());
let entry = match (maybe_entry, maybe_visibility) {
(Some(entry), Some(_)) => RecordEntry::Present(entry),
(None, Some(EntryVisibility::Private)) => RecordEntry::Hidden,
(None, None) => RecordEntry::NotApplicable,
(Some(_), None) => {
unreachable!("Entry is present for an action type which has no entry reference")
}
(None, Some(EntryVisibility::Public)) => RecordEntry::NotStored,
};
Self {
signed_action,
entry,
}
}
/// Access the signature from this record's signed action
pub fn signature(&self) -> &Signature {
self.signed_action.signature()
}
/// Mutable reference to the Action content.
/// This is useless and dangerous in production usage.
/// Guaranteed to make hashes and signatures mismatch whatever the Action is mutated to (at least).
/// This may be useful for tests that rely heavily on mocked and fixturated data.
#[cfg(feature = "test_utils")]
pub fn as_action_mut(&mut self) -> &mut Action {
&mut self.signed_action.hashed.content
}
/// If the Record contains private entry data, set the RecordEntry
/// to Hidden so that it cannot be leaked. If the entry was hidden,
/// return it separately.
pub fn privatized(self) -> (Self, Option<Entry>) {
let (entry, hidden) = if let Some(EntryVisibility::Private) = self
.signed_action
.action()
.entry_data()
.map(|(_, entry_type)| entry_type.visibility())
{
match self.entry {
RecordEntry::Present(entry) => (RecordEntry::Hidden, Some(entry)),
other => (other, None),
}
} else {
(self.entry, None)
};
let privatized = Self {
signed_action: self.signed_action,
entry,
};
(privatized, hidden)
}
/// Access the action address from this record's signed action
pub fn action_address(&self) -> &ActionHash {
self.signed_action.action_address()
}
/// Access the Action from this record's signed action
pub fn action(&self) -> &Action {
self.signed_action.action()
}
/// Access the ActionHashed from this record's signed action portion
pub fn action_hashed(&self) -> &ActionHashed {
&self.signed_action.hashed
}
/// Access the Entry portion of this record as a RecordEntry,
/// which includes the context around the presence or absence of the entry.
pub fn entry(&self) -> &RecordEntry {
&self.entry
}
}
impl<A> Record<A> {
/// Mutable reference to the RecordEntry.
/// This is useless and dangerous in production usage.
/// Guaranteed to make hashes and signatures mismatch whatever the RecordEntry is mutated to (at least).
/// This may be useful for tests that rely heavily on mocked and fixturated data.
#[cfg(feature = "test_utils")]
pub fn as_entry_mut(&mut self) -> &mut RecordEntry {
&mut self.entry
}
/// Break this record into its components
pub fn into_inner(self) -> (A, RecordEntry) {
(self.signed_action, self.entry)
}
/// The inner signed-action
pub fn signed_action(&self) -> &A {
&self.signed_action
}
}
impl RecordEntry {
/// Provides entry data by reference if it exists
///
/// Collapses the enum down to the two possibilities of
/// extant or nonextant Entry data
pub fn as_option(&self) -> Option<&Entry> {
if let RecordEntry::Present(ref entry) = self {
Some(entry)
} else {
None
}
}
/// Provides entry data as owned value if it exists.
///
/// Collapses the enum down to the two possibilities of
/// extant or nonextant Entry data
pub fn into_option(self) -> Option<Entry> {
if let RecordEntry::Present(entry) = self {
Some(entry)
} else {
None
}
}
/// Provides deserialized app entry if it exists
///
/// same as as_option but handles deserialization
/// anything other tha RecordEntry::Present returns None
/// a present entry that fails to deserialize cleanly is an error
/// a present entry that deserializes cleanly is returned as the provided type A
pub fn to_app_option<A: TryFrom<SerializedBytes, Error = SerializedBytesError>>(
&self,
) -> Result<Option<A>, SerializedBytesError> {
match self.as_option() {
Some(Entry::App(eb)) => Ok(Some(A::try_from(SerializedBytes::from(eb.to_owned()))?)),
_ => Ok(None),
}
}
/// Provides CapGrantEntry if it exists
///
/// same as as_option but handles cap grants
/// anything other tha RecordEntry::Present for a Entry::CapGrant returns None
pub fn to_grant_option(&self) -> Option<crate::entry::CapGrantEntry> {
match self.as_option() {
Some(Entry::CapGrant(cap_grant_entry)) => Some(cap_grant_entry.to_owned()),
_ => None,
}
}
}
#[cfg(feature = "test_utils")]
impl<T> SignedHashed<T>
where
T: HashableContent,
<T as holo_hash::HashableContent>::HashType: holo_hash::hash_type::HashTypeSync,
{
/// Create a new signed and hashed content by hashing the content.
pub fn new(content: T, signature: Signature) -> Self {
let hashed = HoloHashed::from_content_sync(content);
Self { hashed, signature }
}
}
impl<T> std::hash::Hash for SignedHashed<T>
where
T: HashableContent,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.signature.hash(state);
self.as_hash().hash(state);
}
}
impl<T> std::cmp::PartialEq for SignedHashed<T>
where
T: HashableContent,
{
fn eq(&self, other: &Self) -> bool {
self.hashed == other.hashed && self.signature == other.signature
}
}
impl<T> SignedHashed<T>
where
T: HashableContent,
{
/// Destructure into a [`HoloHashed`] and [`Signature`].
pub fn into_inner(self) -> (HoloHashed<T>, Signature) {
(self.hashed, self.signature)
}
/// Access the already-calculated hash stored in this wrapper type.
pub fn as_hash(&self) -> &HoloHashOf<T> {
&self.hashed.hash
}
/// Create with an existing signature.
pub fn with_presigned(hashed: HoloHashed<T>, signature: Signature) -> Self {
Self { hashed, signature }
}
/// Access the signature portion.
pub fn signature(&self) -> &Signature {
&self.signature
}
}
impl SignedActionHashed {
/// Access the Action Hash.
pub fn action_address(&self) -> &ActionHash {
&self.hashed.hash
}
/// Access the Action portion.
pub fn action(&self) -> &Action {
&self.hashed.content
}
/// Create a new SignedActionHashed from a type that implements into `Action` and
/// has the same hash bytes.
/// The caller must make sure the hash does not change.
pub fn raw_from_same_hash<T>(other: SignedHashed<T>) -> Self
where
T: Into<Action>,
T: HashableContent<HashType = holo_hash::hash_type::Action>,
{
let SignedHashed {
hashed: HoloHashed { content, hash },
signature,
} = other;
let action = content.into();
let hashed = ActionHashed::with_pre_hashed(action, hash);
Self { hashed, signature }
}
}
impl<T> From<SignedHashed<T>> for HoloHashed<T>
where
T: HashableContent,
{
fn from(sh: SignedHashed<T>) -> HoloHashed<T> {
sh.hashed
}
}
impl From<ActionHashed> for Action {
fn from(action_hashed: ActionHashed) -> Action {
action_hashed.into_content()
}
}
impl From<SignedActionHashed> for Action {
fn from(signed_action_hashed: SignedActionHashed) -> Action {
ActionHashed::from(signed_action_hashed).into()
}
}
impl From<Record> for Option<Entry> {
fn from(e: Record) -> Self {
e.entry.into_option()
}
}
impl TryFrom<Record> for CreateLink {
type Error = WrongActionError;
fn try_from(value: Record) -> Result<Self, Self::Error> {
value
.into_inner()
.0
.into_inner()
.0
.into_content()
.try_into()
}
}
impl TryFrom<Record> for DeleteLink {
type Error = WrongActionError;
fn try_from(value: Record) -> Result<Self, Self::Error> {
value
.into_inner()
.0
.into_inner()
.0
.into_content()
.try_into()
}
}
#[cfg(feature = "test_utils")]
impl<'a, T> arbitrary::Arbitrary<'a> for SignedHashed<T>
where
T: HashableContent,
T: arbitrary::Arbitrary<'a>,
<T as holo_hash::HashableContent>::HashType: holo_hash::PrimitiveHashType,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self {
hashed: HoloHashed::<T>::arbitrary(u)?,
signature: Signature::arbitrary(u)?,
})
}
}