Skip to main content

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}
27
28impl From<&ChainInfo> for Chain {
29    fn from(info: &ChainInfo) -> Self {
30        Self {
31            owner: None,
32            block_hash: info.block_hash,
33            next_block_height: info.next_block_height,
34            timestamp: info.timestamp,
35            pending_proposal: None,
36            epoch: Some(info.epoch),
37        }
38    }
39}
40
41impl From<ChainInfo> for Chain {
42    fn from(info: ChainInfo) -> Self {
43        Self::from(&info)
44    }
45}
46
47impl From<&ChainDescription> for Chain {
48    fn from(description: &ChainDescription) -> Self {
49        Self::new(None, description.config().epoch, description.timestamp())
50    }
51}
52
53impl From<ChainDescription> for Chain {
54    fn from(description: ChainDescription) -> Self {
55        (&description).into()
56    }
57}
58
59impl Chain {
60    /// Creates a chain that we haven't interacted with before.
61    pub fn new(owner: Option<AccountOwner>, current_epoch: Epoch, now: Timestamp) -> Self {
62        Self {
63            owner,
64            block_hash: None,
65            timestamp: now,
66            next_block_height: BlockHeight::ZERO,
67            pending_proposal: None,
68            epoch: Some(current_epoch),
69        }
70    }
71}
72
73/// A trait for the wallet (i.e. set of chain states) tracked by the client.
74#[cfg_attr(not(web), trait_variant::make(Send))]
75pub trait Wallet {
76    type Error: std::error::Error + Send + Sync;
77    async fn get(&self, id: ChainId) -> Result<Option<Chain>, Self::Error>;
78    async fn remove(&self, id: ChainId) -> Result<Option<Chain>, Self::Error>;
79    fn items(&self) -> impl Stream<Item = Result<(ChainId, Chain), Self::Error>>;
80    async fn insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error>;
81    async fn try_insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error>;
82
83    /// Modifies a chain in the wallet. Returns `Ok(None)` if the chain doesn't exist.
84    async fn modify(
85        &self,
86        id: ChainId,
87        f: impl FnMut(&mut Chain) + Send,
88    ) -> Result<Option<()>, 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
100impl<W: Deref<Target: Wallet> + linera_base::util::traits::AutoTraits> Wallet for W {
101    type Error = <W::Target as Wallet>::Error;
102
103    async fn get(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
104        self.deref().get(id).await
105    }
106
107    async fn remove(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
108        self.deref().remove(id).await
109    }
110
111    fn items(&self) -> impl Stream<Item = Result<(ChainId, Chain), Self::Error>> {
112        self.deref().items()
113    }
114
115    async fn insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
116        self.deref().insert(id, chain).await
117    }
118
119    async fn try_insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
120        self.deref().try_insert(id, chain).await
121    }
122
123    fn chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
124        self.deref().chain_ids()
125    }
126
127    fn owned_chain_ids(&self) -> impl Stream<Item = Result<ChainId, Self::Error>> {
128        self.deref().owned_chain_ids()
129    }
130
131    async fn modify(
132        &self,
133        id: ChainId,
134        f: impl FnMut(&mut Chain) + Send,
135    ) -> Result<Option<()>, Self::Error> {
136        self.deref().modify(id, f).await
137    }
138}