linera_wallet_json/
wallet.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{
5    iter::IntoIterator,
6    sync::{Arc, RwLock},
7};
8
9use futures::{stream, Stream};
10use linera_base::identifiers::{AccountOwner, ChainId};
11use linera_client::config::GenesisConfig;
12use linera_core::wallet::*;
13use linera_persistent::{self as persistent};
14
15#[derive(serde::Serialize, serde::Deserialize)]
16pub(crate) struct Data {
17    pub chains: Memory,
18    pub default: Arc<RwLock<Option<ChainId>>>,
19    pub genesis_config: GenesisConfig,
20}
21
22pub struct PersistentWallet(persistent::File<Data>);
23
24// TODO(#5081): `persistent` is no longer necessary here, we can move the locking
25// logic right here
26
27impl linera_core::Wallet for PersistentWallet {
28    type Error = persistent::file::Error;
29
30    async fn get(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
31        Ok(self.get(id))
32    }
33
34    async fn remove(&self, id: ChainId) -> Result<Option<Chain>, Self::Error> {
35        self.remove(id)
36    }
37
38    fn items(&self) -> impl Stream<Item = Result<(ChainId, Chain), Self::Error>> {
39        stream::iter(self.items().into_iter().map(Ok))
40    }
41
42    async fn insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
43        self.insert(id, &chain)
44    }
45
46    async fn try_insert(&self, id: ChainId, chain: Chain) -> Result<Option<Chain>, Self::Error> {
47        let chain = self.try_insert(id, chain)?;
48        self.save()?;
49        Ok(chain)
50    }
51
52    async fn modify(
53        &self,
54        id: ChainId,
55        f: impl Fn(&mut Chain) + Send,
56    ) -> Result<Option<()>, Self::Error> {
57        self.mutate(id, f).transpose()
58    }
59}
60
61impl Extend<(ChainId, Chain)> for PersistentWallet {
62    fn extend<It: IntoIterator<Item = (ChainId, Chain)>>(&mut self, chains: It) {
63        for (id, chain) in chains {
64            if self.0.chains.try_insert(id, chain).is_none() {
65                self.try_set_default(id);
66            }
67        }
68    }
69}
70
71impl PersistentWallet {
72    pub fn get(&self, id: ChainId) -> Option<Chain> {
73        self.0.chains.get(id)
74    }
75
76    pub fn remove(&self, id: ChainId) -> Result<Option<Chain>, persistent::file::Error> {
77        let chain = self.0.chains.remove(id);
78        {
79            let mut default = self.0.default.write().unwrap();
80            if *default == Some(id) {
81                *default = None;
82            }
83        }
84        self.0.save()?;
85        Ok(chain)
86    }
87
88    pub fn items(&self) -> Vec<(ChainId, Chain)> {
89        self.0.chains.items()
90    }
91
92    fn try_set_default(&self, id: ChainId) {
93        let mut guard = self.0.default.write().unwrap();
94        if guard.is_none() {
95            *guard = Some(id);
96        }
97    }
98
99    pub fn insert(
100        &self,
101        id: ChainId,
102        chain: &Chain,
103    ) -> Result<Option<Chain>, persistent::file::Error> {
104        let has_owner = chain.owner.is_some();
105        let old_chain = self.0.chains.insert(id, chain.clone());
106        if has_owner {
107            self.try_set_default(id);
108        }
109        self.0.save()?;
110        Ok(old_chain)
111    }
112
113    pub fn try_insert(
114        &self,
115        id: ChainId,
116        chain: Chain,
117    ) -> Result<Option<Chain>, persistent::file::Error> {
118        let chain = self.0.chains.try_insert(id, chain);
119        if chain.is_none() {
120            self.try_set_default(id);
121        }
122        self.save()?;
123        Ok(chain)
124    }
125
126    pub fn create(
127        path: &std::path::Path,
128        genesis_config: GenesisConfig,
129    ) -> Result<Self, persistent::file::Error> {
130        Ok(Self(persistent::File::new(
131            path,
132            Data {
133                chains: Memory::default(),
134                default: Arc::new(RwLock::new(None)),
135                genesis_config,
136            },
137        )?))
138    }
139
140    pub fn read(path: &std::path::Path) -> Result<Self, persistent::file::Error> {
141        Ok(Self(persistent::File::read(path)?))
142    }
143
144    pub fn genesis_config(&self) -> &GenesisConfig {
145        &self.0.genesis_config
146    }
147
148    pub fn genesis_admin_chain_id(&self) -> ChainId {
149        self.0.genesis_config.admin_chain_id()
150    }
151
152    pub fn default_chain(&self) -> Option<ChainId> {
153        *self.0.default.read().unwrap()
154    }
155
156    pub fn set_default_chain(&mut self, id: ChainId) -> Result<(), persistent::file::Error> {
157        assert!(self.0.chains.get(id).is_some());
158        *self.0.default.write().unwrap() = Some(id);
159        self.0.save()
160    }
161
162    pub fn mutate<R>(
163        &self,
164        chain_id: ChainId,
165        mutate: impl Fn(&mut Chain) -> R,
166    ) -> Option<Result<R, persistent::file::Error>> {
167        self.0
168            .chains
169            .mutate(chain_id, mutate)
170            .map(|outcome| self.0.save().map(|()| outcome))
171    }
172
173    pub fn forget_keys(&self, chain_id: ChainId) -> anyhow::Result<AccountOwner> {
174        self.mutate(chain_id, |chain| chain.owner.take())
175            .ok_or_else(|| anyhow::anyhow!("nonexistent chain `{chain_id}`"))??
176            .ok_or_else(|| anyhow::anyhow!("keypair not found for chain `{chain_id}`"))
177    }
178
179    pub fn forget_chain(&self, chain_id: ChainId) -> anyhow::Result<Chain> {
180        let chain = self
181            .0
182            .chains
183            .remove(chain_id)
184            .ok_or_else(|| anyhow::anyhow!("nonexistent chain `{chain_id}`"))?;
185        self.0.save()?;
186        Ok(chain)
187    }
188
189    pub fn save(&self) -> Result<(), persistent::file::Error> {
190        self.0.save()
191    }
192
193    pub fn num_chains(&self) -> usize {
194        self.0.chains.items().len()
195    }
196
197    pub fn chain_ids(&self) -> Vec<ChainId> {
198        self.0.chains.chain_ids()
199    }
200
201    /// Returns the list of all chain IDs for which we have a secret key.
202    pub fn owned_chain_ids(&self) -> Vec<ChainId> {
203        self.0.chains.owned_chain_ids()
204    }
205
206    pub(crate) fn data(&self) -> &Data {
207        &self.0
208    }
209}