Skip to main content

magicblock_account/cow/
mod.rs

1//! Copy-on-write account data with zero-copy access to aligned external storage.
2//!
3//! `borrowed` defines the raw buffer layout and `owned` holds the heap-backed form.
4#![allow(unsafe_op_in_unsafe_fn)]
5
6mod borrowed;
7mod owned;
8
9pub use borrowed::BorrowedAccount;
10pub use owned::{AccountBuilder, OwnedAccount};
11
12use crate::{Account, ReadableAccount, WritableAccount, patch::AccountPatchError};
13use solana_clock::{Epoch, Slot};
14use solana_pubkey::Pubkey;
15use std::{
16    cell::RefCell,
17    ops::{Deref, DerefMut},
18    rc::Rc,
19    sync::Arc,
20};
21
22use CoWAccount::*;
23
24/// Borrowed buffers must be aligned to this many bytes.
25pub const ALIGNMENT: usize = 8;
26/// Bytes in one storage unit.
27pub const STORAGE_UNIT: usize = size_of::<StorageUnit>();
28/// Minimum addressable storage unit for borrowed account images.
29#[repr(C)]
30#[derive(Clone, Copy, Default)]
31pub struct StorageUnit(pub u64);
32
33/// Shared account data that borrows directly from an aligned external buffer
34/// until a write requires promotion to owned heap storage.
35///
36/// Higher layers use `mutable()` to enforce transaction write permissions.
37#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(from = "Account"))]
38#[derive(Clone, Default)]
39pub struct AccountSharedData {
40    /// Backing storage, borrowed until promotion or direct construction.
41    pub(crate) cow: CoWAccount,
42    /// Fields changed through the writable APIs.
43    pub(crate) dirty: DirtyMarkers,
44}
45
46/// Core account state shared by the borrowed and owned representations.
47#[repr(C)]
48#[derive(Clone, Copy, Default, Eq, PartialEq)]
49#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
50pub struct AccountCore {
51    /// Lamport balance.
52    pub(crate) lamports: u64,
53    /// Account owner.
54    pub(crate) owner: Pubkey,
55    /// On-chain slot, at which the account was cloned.
56    pub(crate) slot: Slot,
57    /// Mutually exclusive mode of existence for the account.
58    pub(crate) mode: AccountMode,
59    /// Account state modifier flags.
60    pub(crate) flags: StateFlags,
61    /// Reserved bytes that make the serialized representation deterministic.
62    _padding: [u8; 6],
63}
64
65impl Deref for AccountSharedData {
66    type Target = AccountCore;
67
68    fn deref(&self) -> &Self::Target {
69        match &self.cow {
70            Borrowed(account) => {
71                // SAFETY: `BorrowedAccount` owns the invariant that `core` points at
72                // a live `AccountCore` inside the borrowed buffer.
73                unsafe { account.core.as_ref() }
74            }
75            Owned(account) => &account.core,
76        }
77    }
78}
79
80impl DerefMut for AccountSharedData {
81    fn deref_mut(&mut self) -> &mut Self::Target {
82        match &mut self.cow {
83            Borrowed(account) => {
84                // SAFETY: `&mut self` guarantees unique access to the borrowed image.
85                unsafe { account.core.as_mut() }
86            }
87            Owned(account) => &mut account.core,
88        }
89    }
90}
91
92impl PartialEq for AccountSharedData {
93    fn eq(&self, other: &Self) -> bool {
94        self.deref() == other.deref() && self.cow.data() == other.cow.data()
95    }
96}
97
98impl Eq for AccountSharedData {}
99
100impl PartialEq<OwnedAccount> for AccountSharedData {
101    fn eq(&self, other: &OwnedAccount) -> bool {
102        self.deref() == &other.core && self.cow.data() == other.data.as_slice()
103    }
104}
105
106impl AccountSharedData {
107    /// Returns a reference to the inner copy-on-write representation.
108    pub fn cow(&self) -> &CoWAccount {
109        &self.cow
110    }
111
112    /// Returns mutable access to the inner copy-on-write representation.
113    pub fn cow_mut(&mut self) -> &mut CoWAccount {
114        &mut self.cow
115    }
116
117    /// Returns the account's on-chain slot.
118    pub fn slot(&self) -> Slot {
119        self.slot
120    }
121
122    /// Copies a clean borrowed image into the shadow buffer before mutation.
123    pub fn translate(&mut self) {
124        if self.dirty() {
125            return;
126        }
127        if let Borrowed(ref mut acc) = self.cow {
128            // SAFETY: this runs before the first dirty marker, so the borrowed
129            // view still points at the active image selected by `init`.
130            unsafe { acc.translate() };
131        }
132    }
133
134    /// Returns an owned copy of the current account state.
135    pub fn owned(&self) -> OwnedAccount {
136        match self.cow() {
137            Borrowed(a) => a.into(),
138            Owned(a) => a.clone(),
139        }
140    }
141
142    /// Returns whether the current transaction may leave the account modified.
143    ///
144    /// Mutable modes are always accepted. `Transient` and `Closed` are accepted
145    /// only when this transaction performed the corresponding mode transition.
146    /// This is a transaction-final writeback predicate, not permission for a new
147    /// program mutation; instruction checks must use [`AccountMode::mutable`].
148    pub fn mutable(&self) -> bool {
149        self.mode.mutable()
150            || matches!(self.mode, AccountMode::Transient | AccountMode::Closed)
151                && self.dirty.contains(DirtyMarkers::MODE)
152    }
153
154    /// Returns the account's exact lifecycle mode.
155    pub fn mode(&self) -> AccountMode {
156        self.mode
157    }
158
159    /// Returns `true` when the account is in `mode`.
160    pub fn is(&self, mode: AccountMode) -> bool {
161        self.mode == mode
162    }
163
164    /// Returns the account modifier flags.
165    pub fn flags(&self) -> &StateFlags {
166        &self.flags
167    }
168
169    /// Returns the dirty-field markers.
170    pub fn markers(&self) -> &DirtyMarkers {
171        &self.dirty
172    }
173
174    /// Marks the data buffer as modified.
175    pub(crate) fn mark_data_dirty(&mut self) {
176        self.dirty.insert(DirtyMarkers::DATA);
177    }
178
179    /// Returns `true` when the owned buffer has more than one strong reference.
180    pub fn is_shared(&self) -> bool {
181        self.cow.is_shared()
182    }
183
184    /// Returns `true` if any field has been modified.
185    pub fn dirty(&self) -> bool {
186        self.dirty.intersects(DirtyMarkers::all())
187    }
188
189    /// Returns the current data capacity.
190    pub fn capacity(&self) -> usize {
191        self.cow.capacity()
192    }
193
194    /// Returns a shared owned copy of the current data bytes.
195    pub fn data_clone(&self) -> Arc<Vec<u8>> {
196        self.cow.data_clone()
197    }
198
199    /// Resizes the account data.
200    pub fn resize(&mut self, len: usize, val: u8) {
201        self.translate();
202        self.mark_data_dirty();
203        self.cow.resize(len, val);
204    }
205
206    /// Appends bytes to the account data.
207    pub fn extend_from_slice(&mut self, data: &[u8]) {
208        self.translate();
209        self.mark_data_dirty();
210        self.cow.extend_from_slice(data);
211    }
212
213    /// Replaces the account data with the provided bytes.
214    pub fn set_data_from_slice(&mut self, data: &[u8]) {
215        self.translate();
216        self.mark_data_dirty();
217        self.cow.set_data_from_slice(data);
218    }
219
220    /// Applies mode and slot as one validated lifecycle transition.
221    ///
222    /// Mode pairs determine whether slots may stay equal or must advance.
223    /// Authoritative modes cannot be reapplied, even at a newer slot.
224    /// Validation precedes translation, so an error leaves account state and
225    /// dirty markers unchanged.
226    pub fn set_lifecycle(
227        &mut self,
228        mode: AccountMode,
229        slot: Slot,
230    ) -> Result<(), AccountPatchError> {
231        self.mode.validate_transition(mode, self.slot, slot)?;
232        self.translate();
233        if self.mode != mode {
234            self.dirty.insert(DirtyMarkers::MODE);
235            self.mode = mode;
236        }
237        self.dirty.insert(DirtyMarkers::SLOT);
238        self.slot = slot;
239        Ok(())
240    }
241
242    /// Writes bytes at `offset`, extending and zero-filling as needed.
243    pub(crate) fn set_data_at(&mut self, offset: usize, data: &[u8]) {
244        self.translate();
245        self.mark_data_dirty();
246        let len = self.data().len();
247        if offset > len {
248            // Grow to `offset`, zero-filling the gap; the write below then
249            // appends `data` past it via `extend_from_slice`.
250            self.resize(offset, 0);
251        }
252
253        // Write the overlap in place, then append any remaining tail. This
254        // keeps borrowed buffers on the fast path when the write fits.
255        let n = self.data().len().saturating_sub(offset).min(data.len());
256        self.data_as_mut_slice()[offset..offset + n].copy_from_slice(&data[..n]);
257        self.extend_from_slice(&data[n..]);
258    }
259
260    /// Replaces all state flags and marks them dirty when the value changes.
261    pub fn set_flags(&mut self, flags: StateFlags) {
262        if self.flags == flags {
263            return;
264        }
265        self.translate();
266        self.dirty.set(DirtyMarkers::FLAGS, true);
267        self.flags = flags;
268    }
269
270    /// Creates a new owned shared-data account with zero-filled data.
271    pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
272        AccountBuilder::default()
273            .lamports(lamports)
274            .data(vec![0; space])
275            .owner(*owner)
276            .build()
277    }
278    /// Creates a new shared-data account wrapped in a `RefCell`.
279    pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
280        Rc::new(RefCell::new(Self::new(lamports, space, owner)))
281    }
282
283    /// Creates a new account with serialized data.
284    #[cfg(feature = "bincode")]
285    pub fn new_data<T: serde::Serialize>(
286        lamports: u64,
287        state: &T,
288        owner: &Pubkey,
289    ) -> Result<Self, bincode::Error> {
290        let data = bincode::serialize(state)?;
291        Ok(Self::create_from_existing_shared_data(
292            lamports,
293            Arc::new(data),
294            *owner,
295            false,
296            Epoch::default(),
297        ))
298    }
299
300    /// Creates a new serialized account wrapped in a `RefCell`.
301    #[cfg(feature = "bincode")]
302    pub fn new_ref_data<T: serde::Serialize>(
303        lamports: u64,
304        state: &T,
305        owner: &Pubkey,
306    ) -> Result<RefCell<Self>, bincode::Error> {
307        Self::new_data(lamports, state, owner).map(RefCell::new)
308    }
309
310    /// Creates a new fixed-size account with serialized data.
311    #[cfg(feature = "bincode")]
312    pub fn new_data_with_space<T: serde::Serialize>(
313        lamports: u64,
314        state: &T,
315        space: usize,
316        owner: &Pubkey,
317    ) -> Result<Self, bincode::Error> {
318        let mut account = Self::new(lamports, space, owner);
319        crate::codec::serialize_data(&mut account, state)?;
320        Ok(account)
321    }
322
323    /// Creates a new fixed-size serialized account wrapped in a `RefCell`.
324    #[cfg(feature = "bincode")]
325    pub fn new_ref_data_with_space<T: serde::Serialize>(
326        lamports: u64,
327        state: &T,
328        space: usize,
329        owner: &Pubkey,
330    ) -> Result<RefCell<Self>, bincode::Error> {
331        Self::new_data_with_space(lamports, state, space, owner).map(RefCell::new)
332    }
333
334    /// Creates a new shared-data account.
335    ///
336    /// `rent_epoch` is ignored because this type does not store it.
337    pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, _: Epoch) -> Self {
338        Self::new(lamports, space, owner)
339    }
340
341    /// Deserializes the account data as `T`.
342    #[cfg(feature = "bincode")]
343    pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
344        crate::codec::deserialize_data(self)
345    }
346
347    /// Serializes `state` into the existing account data buffer.
348    #[cfg(feature = "bincode")]
349    pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
350        crate::codec::serialize_data(self, state)
351    }
352
353    /// Creates an owned shared-data account from existing shared bytes.
354    ///
355    /// `rent_epoch` is ignored because this type does not store it.
356    pub fn create_from_existing_shared_data(
357        lamports: u64,
358        data: Arc<Vec<u8>>,
359        owner: Pubkey,
360        executable: bool,
361        _: Epoch,
362    ) -> Self {
363        AccountBuilder::default()
364            .lamports(lamports)
365            .data(data)
366            .owner(owner)
367            .executable(executable)
368            .build()
369    }
370}
371
372bitflags::bitflags! {
373    /// Account state modifier flags.
374    #[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
375    #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
376    pub struct StateFlags: u8 {
377        /// Executable account data.
378        const EXECUTABLE = 1 << 0;
379    }
380
381    /// Bits that record which fields changed through `AccountSharedData`.
382    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
383    pub struct DirtyMarkers: u8 {
384        /// Owner changed.
385        const OWNER    = 1 << 0;
386        /// Lamports changed.
387        const LAMPORTS = 1 << 1;
388        /// Mode changed.
389        const MODE     = 1 << 2;
390        /// State flags changed.
391        const FLAGS    = 1 << 3;
392        /// Slot changed.
393        const SLOT     = 1 << 4;
394        /// Data bytes changed.
395        const DATA     = 1 << 5;
396    }
397}
398
399/// `wincode` codec for `StateFlags`, which is a `bitflags!` newtype and so
400/// cannot use the derives. Routed through `bincode`/`serde`, which encodes the
401/// single bits byte identically to a plain `u8`.
402#[cfg(feature = "wincode")]
403const _: () = {
404    use core::mem::MaybeUninit;
405    use wincode::{
406        ReadError, ReadResult, SchemaRead, SchemaWrite, TypeMeta, WriteError, WriteResult,
407        config::ConfigCore,
408        io::{Reader, Writer},
409    };
410
411    // SAFETY: encodes exactly one byte; matches `TYPE_META` / `size_of`.
412    unsafe impl<C: ConfigCore> SchemaWrite<C> for StateFlags {
413        type Src = StateFlags;
414        const TYPE_META: TypeMeta = TypeMeta::Static { size: 1, zero_copy: false };
415
416        fn size_of(_: &Self::Src) -> WriteResult<usize> {
417            Ok(1)
418        }
419
420        fn write(mut writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
421            let bytes = bincode::serialize(src).map_err(|_| WriteError::Custom("StateFlags"))?;
422            writer.write(&bytes)?;
423            Ok(())
424        }
425    }
426
427    // SAFETY: consumes exactly one byte; matches `TYPE_META`.
428    unsafe impl<'de, C: ConfigCore> SchemaRead<'de, C> for StateFlags {
429        type Dst = StateFlags;
430        const TYPE_META: TypeMeta = TypeMeta::Static { size: 1, zero_copy: false };
431
432        fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
433            let bytes = reader.take_array::<1>()?;
434            dst.write(bincode::deserialize(&bytes).map_err(|_| ReadError::Custom("StateFlags"))?);
435            Ok(())
436        }
437    }
438};
439
440/// Backing storage for `AccountSharedData`.
441#[derive(PartialEq, Eq)]
442pub enum CoWAccount {
443    /// Borrowed image, a view into static backing buffer.
444    Borrowed(BorrowedAccount),
445    /// Heap-owned image.
446    Owned(OwnedAccount),
447}
448
449impl Clone for CoWAccount {
450    fn clone(&self) -> Self {
451        match self {
452            Borrowed(acc) => Self::Owned(acc.into()),
453            Owned(acc) => Self::Owned(acc.clone()),
454        }
455    }
456}
457
458impl CoWAccount {
459    /// Promotes borrowed storage to the owned form.
460    pub(crate) fn promote(&mut self) {
461        let Self::Borrowed(account) = self else {
462            return;
463        };
464        *self = Self::Owned(account.deref().into());
465    }
466
467    /// Returns the current data slice.
468    pub(crate) fn data(&self) -> &[u8] {
469        match self {
470            Self::Borrowed(account) => &account.data,
471            Self::Owned(account) => &account.data,
472        }
473    }
474
475    /// Returns `true` when the heap buffer has multiple owners.
476    pub(crate) fn is_shared(&self) -> bool {
477        match self {
478            Self::Borrowed(_) => false,
479            Self::Owned(account) => Arc::strong_count(&account.data) > 1,
480        }
481    }
482
483    /// Returns the current data capacity.
484    pub(crate) fn capacity(&self) -> usize {
485        match self {
486            Self::Borrowed(account) => account.data.capacity(),
487            Self::Owned(account) => account.data.capacity(),
488        }
489    }
490
491    /// Returns a shared owned copy of the current data bytes.
492    pub(crate) fn data_clone(&self) -> Arc<Vec<u8>> {
493        match self {
494            Self::Borrowed(account) => Arc::new(account.data.to_vec()),
495            Self::Owned(account) => Arc::clone(&account.data),
496        }
497    }
498
499    /// Returns mutable data, promoting borrowed storage only when needed.
500    pub(crate) fn data_mut(&mut self) -> &mut [u8] {
501        match self {
502            Self::Borrowed(account) => &mut account.data,
503            Self::Owned(account) => Arc::<Vec<u8>>::make_mut(&mut account.data).as_mut_slice(),
504        }
505    }
506
507    /// Reserves additional space for the account data.
508    pub fn reserve(&mut self, additional: usize) {
509        if let Self::Borrowed(a) = self
510            && a.data.spare() >= additional
511        {
512            return;
513        }
514        self.promote();
515        if let Self::Owned(account) = self {
516            Arc::make_mut(&mut account.data).reserve(additional);
517        }
518    }
519
520    /// Resizes the account data.
521    pub(crate) fn resize(&mut self, len: usize, val: u8) {
522        if let Self::Borrowed(a) = self
523            && len <= a.data.capacity()
524        {
525            // SAFETY: this stays in the borrowed image only while the resized
526            // range fits within the borrowed capacity.
527            unsafe { a.data.resize(len, val) };
528            return;
529        }
530
531        self.promote();
532        if let Self::Owned(account) = self {
533            Arc::make_mut(&mut account.data).resize(len, val);
534        }
535    }
536
537    /// Appends bytes to the account data.
538    pub(crate) fn extend_from_slice(&mut self, data: &[u8]) {
539        self.reserve(data.len());
540
541        match self {
542            Self::Borrowed(account) => {
543                // SAFETY: `reserve` keeps the borrowed image only when the appended
544                // bytes fit in the remaining borrowed capacity.
545                unsafe { account.data.extend(data) };
546            }
547            Self::Owned(account) => Arc::make_mut(&mut account.data).extend_from_slice(data),
548        }
549    }
550
551    /// Replaces the account data with the provided bytes.
552    pub(crate) fn set_data_from_slice(&mut self, data: &[u8]) {
553        let additional = data.len().saturating_sub(self.data().len());
554        self.reserve(additional);
555
556        match self {
557            Self::Borrowed(account) => {
558                // SAFETY: `reserve` keeps the borrowed image only when the
559                // replacement bytes fit in the borrowed capacity.
560                unsafe { account.data.set(data) };
561            }
562            Self::Owned(account) => {
563                let data_buf = Arc::make_mut(&mut account.data);
564                data_buf.clear();
565                data_buf.extend_from_slice(data);
566            }
567        }
568    }
569}
570
571/// Mutually exclusive modes an account can occupy in the ephemeral rollup (ER).
572#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
573#[repr(u8)]
574#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
575#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
576pub enum AccountMode {
577    /// Empty account (not found on chain) used to avoid frequent chain syncs.
578    #[default]
579    Placeholder = 0,
580    /// Not writable by users (exists on chain, but not delegated)
581    ReadOnly,
582    /// Internal account used for sysvars, features, and precompiles.
583    System,
584    /// Account delegated to the current ER node instance.
585    Delegated,
586    /// Account that exists only inside the ER.
587    Ephemeral,
588    /// Temporary state during mode transitions (e.g. delegated -> readonly).
589    Transient,
590    /// Closed account that should be removed from storage.
591    Closed = 255,
592}
593
594impl AccountMode {
595    /// Returns whether a privileged lifecycle operation may apply `to` at
596    /// `to_slot`, including same-mode refreshes and slot ordering.
597    ///
598    /// Only placeholder, read-only, and system accounts permit same-mode
599    /// refreshes, and those require a newer slot. Slots may never regress.
600    pub fn allows_transition(self, to: Self, from_slot: Slot, to_slot: Slot) -> bool {
601        self.validate_transition(to, from_slot, to_slot).is_ok()
602    }
603
604    fn validate_transition(
605        self,
606        to: Self,
607        from_slot: Slot,
608        to_slot: Slot,
609    ) -> Result<(), AccountPatchError> {
610        use AccountMode::*;
611        let valid_slot = match (self, to) {
612            (Placeholder, ReadOnly | System | Delegated | Ephemeral | Closed)
613            | (ReadOnly, Delegated | Ephemeral | Closed)
614            | (Delegated, Transient)
615            | (Transient, ReadOnly | Placeholder)
616            | (Ephemeral, Closed) => to_slot >= from_slot,
617            // Refreshes, observed disappearance, and redelegation need newer evidence.
618            (Placeholder, Placeholder)
619            | (ReadOnly, ReadOnly | Placeholder)
620            | (System, System)
621            | (Transient, Delegated) => to_slot > from_slot,
622            _ => return Err(AccountPatchError::InvalidModeTransition { from: self, to }),
623        };
624        if !valid_slot {
625            return Err(AccountPatchError::InvalidSlotTransition { from: from_slot, to: to_slot });
626        }
627        Ok(())
628    }
629
630    /// Returns `true` for modes that may be mutated by user programs.
631    pub fn mutable(&self) -> bool {
632        use AccountMode::*;
633        matches!(self, Delegated | Ephemeral)
634    }
635
636    /// Returns `true` for modes whose state is authoritative in this engine.
637    pub fn authoritative(&self) -> bool {
638        use AccountMode::*;
639        matches!(self, Delegated | Ephemeral | Transient)
640    }
641}
642
643/// Read wrapper that retries borrowed account reads when a concurrent publish
644/// changes the backing image.
645pub struct AccountSeqLock {
646    account: AccountSharedData,
647    sequence: Option<u32>,
648}
649
650impl AccountSeqLock {
651    /// Creates a read lock with the sequence that matches the current account view.
652    pub fn new(account: AccountSharedData) -> Self {
653        let mut sequence = None;
654        if let Borrowed(ref acc) = account.cow {
655            sequence.replace(acc.version);
656        }
657        Self { account, sequence }
658    }
659
660    /// Runs `reader` against a stable account image.
661    ///
662    /// For borrowed accounts, the sequence is checked after the read. If a
663    /// writer published a new image meanwhile, the account view is reset to that
664    /// active image and the read is retried.
665    pub fn read<F, R>(&mut self, reader: F) -> R
666    where
667        F: Fn(&AccountSharedData) -> R,
668    {
669        loop {
670            // sequence is always present for borrowed accounts
671            let pre = self.sequence.unwrap_or_default();
672            let result = reader(&self.account);
673            match self.account.cow_mut() {
674                Borrowed(acc) => {
675                    let post = acc.sequence();
676                    if pre == post {
677                        return result;
678                    }
679                    // SAFETY: a changed sequence means the active image may have
680                    // moved, so the borrowed view must be repointed before retrying.
681                    unsafe { acc.reset() };
682                    self.sequence = Some(acc.version);
683                }
684                Owned(_) => return result,
685            }
686        }
687    }
688}
689
690impl Default for CoWAccount {
691    fn default() -> Self {
692        Self::Owned(OwnedAccount::default())
693    }
694}
695
696/// Wraps an owned account in `AccountSharedData`.
697impl From<OwnedAccount> for AccountSharedData {
698    fn from(value: OwnedAccount) -> Self {
699        Self {
700            cow: Owned(value),
701            dirty: DirtyMarkers::default(),
702        }
703    }
704}
705
706/// Wraps a borrowed account in `AccountSharedData`.
707impl From<BorrowedAccount> for AccountSharedData {
708    fn from(value: BorrowedAccount) -> Self {
709        Self {
710            cow: Borrowed(value),
711            dirty: DirtyMarkers::default(),
712        }
713    }
714}
715
716/// Converts a plain `Account` into shared data.
717impl From<Account> for AccountSharedData {
718    fn from(value: Account) -> Self {
719        AccountBuilder::default()
720            .lamports(value.lamports)
721            .data(value.data)
722            .owner(value.owner)
723            .executable(value.executable)
724            .build()
725    }
726}
727
728/// We only access AccountSharedData via transaction lock in the
729/// execution layer or with a SeqLock semantics outside of execution
730unsafe impl Sync for AccountSharedData {}