linera_core/environment/wallet/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::ops::Deref;
5
6use futures::{Stream, StreamExt as _, TryStreamExt as _};
7use linera_base::{
8    crypto::CryptoHash,
9    data_types::{BlockHeight, ChainDescription, Epoch, Timestamp},
10    identifiers::{AccountOwner, ChainId},
11};
12
13use crate::{client::PendingProposal, data_types::ChainInfo};
14
15mod memory;
16pub use memory::Memory;
17
18#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
19pub struct Chain {
20    pub owner: Option<AccountOwner>,
21    pub block_hash: Option<CryptoHash>,
22    pub next_block_height: BlockHeight,
23    pub timestamp: Timestamp,
24    pub pending_proposal: Option<PendingProposal>,
25    pub epoch: Option<Epoch>,
26    /// If true, we only follow this chain's blocks without downloading sender chain blocks
27    /// or participating in consensus rounds. Use this for chains we're interested in observing
28    /// but don't intend to propose blocks for.
29    #[serde(default)]
30    pub follow_only: bool,
31}
32
33impl From<&ChainInfo> for Chain {
34    fn from(info: &ChainInfo) -> Self {
35        Self {
36            owner: None,
37            block_hash: info.block_hash,
38            next_block_height: info.next_block_height,
39            timestamp: info.timestamp,
40            pending_proposal: None,
41            epoch: Some(info.epoch),
42            follow_only: false,
43        }
44    }
45}
46
47impl From<ChainInfo> for Chain {
48    fn from(info: ChainInfo) -> Self {
49        Self::from(&info)
50    }
51}
52
53impl From<&ChainDescription> for Chain {
54    fn from(description: &ChainDescription) -> Self {
55        Self::new(None, description.config().epoch, description.timestamp())
56    }
57}
58
59impl From<ChainDescription> for Chain {
60    fn from(description: ChainDescription) -> Self {
61        (&description).into()
62    }
63}
64
65impl Chain {
66    /// Create a chain that we haven't interacted with before.
67    pub fn new(owner: Option<AccountOwner>, current_epoch: Epoch, now: Timestamp) -> Self {
68        Self {
69            owner,
70            block_hash: None,
71            timestamp: now,
72            next_block_height: BlockHeight::ZERO,
73            pending_proposal: None,
74            epoch: Some(current_epoch),
75            follow_only: false,
76        }
77    }
78}
79
80/// A trait for the wallet (i.e. set of chain states) tracked by the client.
81#[cfg_attr(not(web), trait_variant::make(Send))]
82pub trait Wallet {
83    type Error: std::error::Error + Send + Sync;
84    async fn get(&self, id: ChainId) -> Result<Option<Chain>, Self::Error>;
85    async fn remove(&self, id: ChainId) -> Result<Option<Chain>, Self::Error>;
86    fn items(&self) -> impl Stream<Item = Result<(ChainId, Chain), Self::Error>>;
87    async fn insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error>;
88    async fn try_insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error>;
89
90    fn chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
91        self.items().map(|result| result.map(|kv| kv.0))
92    }
93
94    fn owned_chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
95        self.items()
96            .try_filter_map(|(id, chain)| async move { Ok(chain.owner.map(|_| id)) })
97    }
98
99    /// Modifies a chain in the wallet. Returns `Ok(None)` if the chain doesn't exist.
100    async fn modify(
101        &self,
102        id: ChainId,
103        f: impl FnMut(&mut Chain) + Send,
104    ) -> Result<Option<()>, Self::Error>;
105}
106
107impl<W: Deref<Target: Wallet> + linera_base::util::traits::AutoTraits> Wallet for W {
108    type Error = <W::Target as Wallet>::Error;
109
110    async fn get(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
111        self.deref().get(id).await
112    }
113
114    async fn remove(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
115        self.deref().remove(id).await
116    }
117
118    fn items(&self) -> impl Stream<Item = Result<(ChainId, Chain), Self::Error>> {
119        self.deref().items()
120    }
121
122    async fn insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
123        self.deref().insert(id, chain).await
124    }
125
126    async fn try_insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
127        self.deref().try_insert(id, chain).await
128    }
129
130    fn chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
131        self.deref().chain_ids()
132    }
133
134    fn owned_chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
135        self.deref().owned_chain_ids()
136    }
137
138    async fn modify(
139        &self,
140        id: ChainId,
141        f: impl FnMut(&mut Chain) + Send,
142    ) -> Result<Option<()>, Self::Error> {
143        self.deref().modify(id, f).await
144    }
145}