794 lines
24 KiB
Rust
Raw Normal View History

use std::{
collections::HashMap,
io::{BufReader, Write as _},
path::Path,
time::Duration,
};
2025-12-08 18:26:35 +03:00
use anyhow::{Context as _, Result};
use common::config::BasicAuth;
use humantime_serde;
2025-11-10 16:29:33 +02:00
use key_protocol::key_management::{
KeyChain,
key_tree::{
chain_index::ChainIndex, keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic,
},
};
use log::warn;
2025-08-19 14:14:09 +03:00
use serde::{Deserialize, Serialize};
use url::Url;
2024-12-03 09:32:35 +02:00
2025-08-19 14:14:09 +03:00
#[derive(Debug, Clone, Serialize, Deserialize)]
2025-09-11 18:32:46 +03:00
pub struct InitialAccountDataPublic {
pub account_id: nssa::AccountId,
2025-08-19 14:14:09 +03:00
pub pub_sign_key: nssa::PrivateKey,
}
2025-02-28 12:32:54 +02:00
2025-09-02 09:01:33 +03:00
#[derive(Debug, Clone, Serialize, Deserialize)]
2025-09-11 18:32:46 +03:00
pub struct PersistentAccountDataPublic {
pub account_id: nssa::AccountId,
2025-11-10 16:29:33 +02:00
pub chain_index: ChainIndex,
pub data: ChildKeysPublic,
2025-09-02 09:01:33 +03:00
}
2025-09-11 18:32:46 +03:00
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitialAccountDataPrivate {
pub account_id: nssa::AccountId,
2025-09-11 18:32:46 +03:00
pub account: nssa_core::account::Account,
pub key_chain: KeyChain,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistentAccountDataPrivate {
pub account_id: nssa::AccountId,
2025-11-10 16:29:33 +02:00
pub chain_index: ChainIndex,
pub data: ChildKeysPrivate,
2025-09-11 18:32:46 +03:00
}
2025-11-26 00:27:20 +03:00
// Big difference in enum variants sizes
// however it is improbable, that we will have that much accounts, that it will substantialy affect
// memory
2025-09-11 18:32:46 +03:00
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InitialAccountData {
Public(InitialAccountDataPublic),
2026-03-04 18:42:33 +03:00
Private(Box<InitialAccountDataPrivate>),
2025-09-11 18:32:46 +03:00
}
2025-11-26 00:27:20 +03:00
// Big difference in enum variants sizes
// however it is improbable, that we will have that much accounts, that it will substantialy affect
// memory
2025-09-11 18:32:46 +03:00
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PersistentAccountData {
Public(PersistentAccountDataPublic),
2026-03-04 18:42:33 +03:00
Private(Box<PersistentAccountDataPrivate>),
2025-11-11 12:15:20 +02:00
Preconfigured(InitialAccountData),
2025-09-11 18:32:46 +03:00
}
/// A human-readable label for an account.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Label(String);
impl Label {
2026-03-03 23:21:08 +03:00
#[must_use]
2026-03-09 18:27:56 +03:00
pub const fn new(label: String) -> Self {
Self(label)
}
}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2025-10-28 16:02:30 +02:00
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistentStorage {
pub accounts: Vec<PersistentAccountData>,
pub last_synced_block: u64,
/// Account labels keyed by account ID string (e.g.,
2026-03-10 00:17:43 +03:00
/// "2rnKprXqWGWJTkDZKsQbFXa4ctKRbapsdoTKQFnaVGG8").
#[serde(default)]
pub labels: HashMap<String, Label>,
2025-10-28 16:02:30 +02:00
}
impl PersistentStorage {
pub fn from_path(path: &Path) -> Result<Self> {
2026-03-04 18:42:33 +03:00
#[expect(
clippy::wildcard_enum_match_arm,
reason = "We want to provide a specific error message for not found case"
)]
match std::fs::File::open(path) {
Ok(file) => {
let storage_content = BufReader::new(file);
Ok(serde_json::from_reader(storage_content)?)
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
anyhow::bail!("Not found, please setup roots from config command beforehand");
}
_ => {
anyhow::bail!("IO error {err:#?}");
}
},
}
}
}
2025-09-11 18:32:46 +03:00
impl InitialAccountData {
2026-03-03 23:21:08 +03:00
#[must_use]
pub fn account_id(&self) -> nssa::AccountId {
2025-09-11 18:32:46 +03:00
match &self {
Self::Public(acc) => acc.account_id,
Self::Private(acc) => acc.account_id,
2025-09-11 18:32:46 +03:00
}
}
}
impl PersistentAccountData {
2026-03-03 23:21:08 +03:00
#[must_use]
pub fn account_id(&self) -> nssa::AccountId {
2025-09-11 18:32:46 +03:00
match &self {
Self::Public(acc) => acc.account_id,
Self::Private(acc) => acc.account_id,
Self::Preconfigured(acc) => acc.account_id(),
2025-09-11 18:32:46 +03:00
}
}
}
impl From<InitialAccountDataPublic> for InitialAccountData {
fn from(value: InitialAccountDataPublic) -> Self {
Self::Public(value)
}
}
impl From<InitialAccountDataPrivate> for InitialAccountData {
fn from(value: InitialAccountDataPrivate) -> Self {
2026-03-04 18:42:33 +03:00
Self::Private(Box::new(value))
2025-09-11 18:32:46 +03:00
}
}
impl From<PersistentAccountDataPublic> for PersistentAccountData {
fn from(value: PersistentAccountDataPublic) -> Self {
Self::Public(value)
}
}
impl From<PersistentAccountDataPrivate> for PersistentAccountData {
fn from(value: PersistentAccountDataPrivate) -> Self {
2026-03-04 18:42:33 +03:00
Self::Private(Box::new(value))
2025-09-11 18:32:46 +03:00
}
}
2025-11-11 12:15:20 +02:00
impl From<InitialAccountData> for PersistentAccountData {
fn from(value: InitialAccountData) -> Self {
Self::Preconfigured(value)
}
}
2025-02-28 12:32:54 +02:00
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GasConfig {
2026-03-10 00:17:43 +03:00
/// Gas spent per deploying one byte of data.
2025-02-28 12:32:54 +02:00
pub gas_fee_per_byte_deploy: u64,
2026-03-10 00:17:43 +03:00
/// Gas spent per reading one byte of data in VM.
2025-02-28 12:32:54 +02:00
pub gas_fee_per_input_buffer_runtime: u64,
2026-03-10 00:17:43 +03:00
/// Gas spent per one byte of contract data in runtime.
2025-02-28 12:32:54 +02:00
pub gas_fee_per_byte_runtime: u64,
2026-03-10 00:17:43 +03:00
/// Cost of one gas of runtime in public balance.
2025-02-28 12:32:54 +02:00
pub gas_cost_runtime: u64,
2026-03-10 00:17:43 +03:00
/// Cost of one gas of deployment in public balance.
2025-02-28 12:32:54 +02:00
pub gas_cost_deploy: u64,
2026-03-10 00:17:43 +03:00
/// Gas limit for deployment.
2025-02-28 12:32:54 +02:00
pub gas_limit_deploy: u64,
2026-03-10 00:17:43 +03:00
/// Gas limit for runtime.
2025-02-28 12:32:54 +02:00
pub gas_limit_runtime: u64,
}
2026-02-03 19:04:41 -03:00
#[optfield::optfield(pub WalletConfigOverrides, rewrap, attrs = (derive(Debug, Default, Clone)))]
2024-12-03 09:32:35 +02:00
#[derive(Debug, Clone, Serialize, Deserialize)]
2025-08-11 08:55:08 +03:00
pub struct WalletConfig {
2026-03-10 00:17:43 +03:00
/// Override rust log (env var logging level).
#[serde(skip_serializing_if = "Option::is_none")]
2024-12-03 09:32:35 +02:00
pub override_rust_log: Option<String>,
2026-03-10 00:17:43 +03:00
/// Sequencer URL.
pub sequencer_addr: Url,
2026-03-10 00:17:43 +03:00
/// Sequencer polling duration for new blocks.
#[serde(with = "humantime_serde")]
pub seq_poll_timeout: Duration,
2026-03-10 00:17:43 +03:00
/// Sequencer polling max number of blocks to find transaction.
pub seq_tx_poll_max_blocks: usize,
2026-03-10 00:17:43 +03:00
/// Sequencer polling max number error retries.
2025-08-21 15:58:31 +03:00
pub seq_poll_max_retries: u64,
2026-03-10 00:17:43 +03:00
/// Max amount of blocks to poll in one request.
pub seq_block_poll_max_amount: u64,
2026-03-10 00:17:43 +03:00
/// Initial accounts for wallet.
2025-08-19 14:14:09 +03:00
pub initial_accounts: Vec<InitialAccountData>,
2026-03-10 00:17:43 +03:00
/// Basic authentication credentials.
#[serde(skip_serializing_if = "Option::is_none")]
2025-12-08 18:26:35 +03:00
pub basic_auth: Option<BasicAuth>,
2024-12-03 09:32:35 +02:00
}
2025-10-29 13:23:07 +02:00
impl Default for WalletConfig {
fn default() -> Self {
Self {
override_rust_log: None,
sequencer_addr: "http://127.0.0.1:3040".parse().unwrap(),
seq_poll_timeout: Duration::from_secs(12),
seq_tx_poll_max_blocks: 5,
2025-10-29 13:23:07 +02:00
seq_poll_max_retries: 5,
seq_block_poll_max_amount: 100,
2025-12-08 18:26:35 +03:00
basic_auth: None,
2025-10-29 13:23:07 +02:00
initial_accounts: {
let init_acc_json = r#"
2026-02-27 18:30:54 -05:00
[
2025-10-29 13:23:07 +02:00
{
"Public": {
2026-02-27 18:30:54 -05:00
"account_id": "CbgR6tj5kWx5oziiFptM7jMvrQeYY3Mzaao6ciuhSr2r",
2025-10-29 13:23:07 +02:00
"pub_sign_key": [
2026-03-09 11:50:37 -04:00
127,
39,
48,
2026-02-27 18:30:54 -05:00
152,
2026-03-09 11:50:37 -04:00
242,
91,
113,
230,
192,
5,
169,
81,
159,
38,
120,
218,
141,
28,
127,
2026-02-27 18:30:54 -05:00
1,
2026-03-09 11:50:37 -04:00
246,
162,
119,
120,
226,
217,
148,
138,
189,
2026-02-27 18:30:54 -05:00
249,
2026-03-09 11:50:37 -04:00
1,
251
2025-10-29 13:23:07 +02:00
]
}
},
{
"Public": {
2026-02-27 18:30:54 -05:00
"account_id": "2RHZhw9h534Zr3eq2RGhQete2Hh667foECzXPmSkGni2",
2025-10-29 13:23:07 +02:00
"pub_sign_key": [
2026-03-09 11:50:37 -04:00
244,
52,
248,
116,
2025-10-29 13:23:07 +02:00
23,
2026-03-09 11:50:37 -04:00
32,
1,
69,
134,
174,
67,
53,
109,
42,
236,
98,
87,
218,
8,
98,
34,
246,
4,
221,
183,
93,
105,
115,
59,
134,
252,
76
2025-10-29 13:23:07 +02:00
]
}
},
{
"Private": {
2026-02-27 18:30:54 -05:00
"account_id": "HWkW5qd4XK3me6sCAb4bfPj462k33DjtKtEcYpuzNwB",
2025-10-29 13:23:07 +02:00
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 10000,
"data": [],
"nonce": 0
},
"key_chain": {
"secret_spending_key": [
2026-02-27 18:30:54 -05:00
14,
202,
241,
109,
32,
181,
152,
140,
76,
153,
108,
2026-02-16 19:48:43 -05:00
57,
2026-02-27 18:30:54 -05:00
77,
192,
181,
97,
2026-02-16 19:48:43 -05:00
108,
2026-02-27 18:30:54 -05:00
144,
122,
45,
219,
5,
203,
193,
82,
123,
83,
34,
250,
214,
137,
63
2025-10-29 13:23:07 +02:00
],
"private_key_holder": {
"nullifier_secret_key": [
2026-02-27 18:30:54 -05:00
174,
56,
101,
30,
248,
249,
100,
2026-02-16 19:48:43 -05:00
0,
122,
2026-02-27 18:30:54 -05:00
199,
209,
246,
58,
163,
223,
146,
59,
2026-02-16 19:48:43 -05:00
143,
2026-02-27 18:30:54 -05:00
78,
95,
41,
186,
106,
187,
53,
63,
75,
244,
233,
185,
110,
199
2025-10-29 13:23:07 +02:00
],
2026-01-21 17:27:23 -05:00
"viewing_secret_key": [
2026-02-27 18:30:54 -05:00
251,
85,
223,
73,
142,
127,
134,
132,
185,
210,
100,
103,
198,
108,
229,
80,
176,
211,
2026-02-16 19:48:43 -05:00
249,
2026-02-27 18:30:54 -05:00
114,
110,
7,
225,
17,
7,
69,
204,
32,
47,
242,
103,
247
2025-10-29 13:23:07 +02:00
]
},
2026-03-09 11:50:37 -04:00
"nullifier_public_key": [
2026-02-27 18:30:54 -05:00
139,
19,
158,
2026-02-16 19:48:43 -05:00
11,
2026-02-27 18:30:54 -05:00
155,
2026-02-16 19:48:43 -05:00
231,
2026-02-27 18:30:54 -05:00
85,
206,
132,
228,
220,
114,
145,
89,
113,
156,
238,
142,
242,
74,
182,
91,
43,
100,
6,
190,
31,
15,
31,
88,
96,
204
2025-10-29 13:23:07 +02:00
],
2026-01-21 17:27:23 -05:00
"viewing_public_key": [
2026-02-27 18:30:54 -05:00
3,
136,
153,
50,
191,
184,
135,
36,
29,
107,
57,
9,
218,
135,
249,
213,
118,
215,
118,
173,
30,
137,
116,
77,
17,
86,
62,
154,
31,
173,
19,
167,
211
]
2025-10-29 13:23:07 +02:00
}
}
},
{
"Private": {
2026-02-27 18:30:54 -05:00
"account_id": "HUpbRQ1vEcZv5y6TDYv9tpt1VA64ji2v4RDLJfK2rpZn",
2025-10-29 13:23:07 +02:00
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 20000,
"data": [],
"nonce": 0
},
"key_chain": {
"secret_spending_key": [
2026-02-27 18:30:54 -05:00
32,
162,
244,
221,
2,
133,
168,
250,
240,
52,
92,
187,
157,
116,
249,
203,
143,
194,
214,
112,
115,
142,
153,
78,
241,
173,
103,
242,
192,
196,
29,
133
],
2025-10-29 13:23:07 +02:00
"private_key_holder": {
"nullifier_secret_key": [
2026-02-27 18:30:54 -05:00
188,
235,
121,
54,
131,
206,
7,
215,
94,
231,
102,
22,
12,
27,
253,
161,
248,
206,
41,
160,
206,
149,
5,
217,
127,
235,
154,
230,
198,
232,
102,
31
2025-10-29 13:23:07 +02:00
],
2026-01-21 17:27:23 -05:00
"viewing_secret_key": [
2026-02-27 18:30:54 -05:00
89,
116,
140,
122,
211,
179,
190,
229,
18,
94,
56,
235,
48,
99,
104,
228,
111,
72,
231,
18,
247,
97,
110,
60,
238,
138,
0,
25,
92,
44,
30,
145
2025-10-29 13:23:07 +02:00
]
},
2026-03-09 11:50:37 -04:00
"nullifier_public_key": [
2026-02-27 18:30:54 -05:00
173,
134,
33,
223,
54,
226,
10,
71,
215,
254,
143,
172,
24,
244,
243,
208,
65,
112,
118,
70,
217,
240,
69,
100,
129,
3,
121,
25,
213,
132,
42,
45
2025-10-29 13:23:07 +02:00
],
2026-01-21 17:27:23 -05:00
"viewing_public_key": [
2026-02-27 18:30:54 -05:00
2,
43,
42,
253,
112,
83,
195,
164,
26,
141,
92,
28,
224,
120,
155,
119,
225,
1,
45,
42,
245,
172,
134,
136,
52,
183,
170,
96,
115,
212,
114,
120,
37
2025-10-29 13:23:07 +02:00
]
}
}
}
]
"#;
serde_json::from_str(init_acc_json).unwrap()
},
}
}
}
impl WalletConfig {
2026-03-09 18:27:56 +03:00
pub fn from_path_or_initialize_default(config_path: &Path) -> Result<Self> {
match std::fs::File::open(config_path) {
Ok(file) => {
let reader = std::io::BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!("Config not found, setting up default config");
let config_home = config_path.parent().ok_or_else(|| {
anyhow::anyhow!(
2026-03-03 23:21:08 +03:00
"Could not get parent directory of config file at {}",
config_path.display()
)
})?;
std::fs::create_dir_all(config_home)?;
2026-03-03 23:21:08 +03:00
println!("Created configs dir at path {}", config_home.display());
let mut file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(config_path)?;
2026-03-09 18:27:56 +03:00
let config = Self::default();
let default_config_serialized = serde_json::to_vec_pretty(&config).unwrap();
file.write_all(&default_config_serialized)?;
println!("Configs set up");
Ok(config)
}
Err(err) => Err(err).context("IO error"),
}
}
pub fn apply_overrides(&mut self, overrides: WalletConfigOverrides) {
2026-03-09 18:27:56 +03:00
let Self {
override_rust_log,
sequencer_addr,
seq_poll_timeout,
seq_tx_poll_max_blocks,
seq_poll_max_retries,
seq_block_poll_max_amount,
initial_accounts,
basic_auth,
} = self;
let WalletConfigOverrides {
override_rust_log: o_override_rust_log,
sequencer_addr: o_sequencer_addr,
seq_poll_timeout: o_seq_poll_timeout,
seq_tx_poll_max_blocks: o_seq_tx_poll_max_blocks,
seq_poll_max_retries: o_seq_poll_max_retries,
seq_block_poll_max_amount: o_seq_block_poll_max_amount,
initial_accounts: o_initial_accounts,
basic_auth: o_basic_auth,
} = overrides;
if let Some(v) = o_override_rust_log {
warn!("Overriding wallet config 'override_rust_log' to {v:#?}");
*override_rust_log = v;
}
if let Some(v) = o_sequencer_addr {
warn!("Overriding wallet config 'sequencer_addr' to {v}");
*sequencer_addr = v;
}
if let Some(v) = o_seq_poll_timeout {
warn!("Overriding wallet config 'seq_poll_timeout' to {v:?}");
*seq_poll_timeout = v;
}
if let Some(v) = o_seq_tx_poll_max_blocks {
warn!("Overriding wallet config 'seq_tx_poll_max_blocks' to {v}");
*seq_tx_poll_max_blocks = v;
}
if let Some(v) = o_seq_poll_max_retries {
warn!("Overriding wallet config 'seq_poll_max_retries' to {v}");
*seq_poll_max_retries = v;
}
if let Some(v) = o_seq_block_poll_max_amount {
warn!("Overriding wallet config 'seq_block_poll_max_amount' to {v}");
*seq_block_poll_max_amount = v;
}
if let Some(v) = o_initial_accounts {
warn!("Overriding wallet config 'initial_accounts' to {v:#?}");
*initial_accounts = v;
}
if let Some(v) = o_basic_auth {
warn!("Overriding wallet config 'basic_auth' to {v:#?}");
*basic_auth = v;
}
}
}