Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add account details endpoint #248

Merged
merged 20 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ args = ["-rf", "miden-node"]
description = "Clone or update miden-node repository and clean up files"
script_runner = "bash"
script = [
'if [ -d miden-node ]; then cd miden-node && git pull ; else git clone --branch next https://github.com/0xPolygonMiden/miden-node.git && cd miden-node; fi',
'if [ -d miden-node ]; then cd miden-node && git checkout next && git pull origin next && cargo update; else git clone --branch next https://github.com/0xPolygonMiden/miden-node.git && cd miden-node && cargo update; fi',
'rm -rf miden-store.sqlite3 miden-store.sqlite3-wal miden-store.sqlite3-shm',
'cargo run --bin $NODE_BINARY --features $NODE_FEATURES_TESTING -- make-genesis --inputs-path node/genesis.toml --force'
]
Expand Down
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
large-error-threshold = 256
too-many-arguments-threshold = 8
13 changes: 12 additions & 1 deletion docs/library.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,18 @@ With the Miden client, you can create and track local (not on-chain) accounts. W
let (new_account, account_seed) = client.new_account(client_template)?;
```

The `AccountTemplate` enum defines which type of account will be created. Note that once an account is created, it will be kept locally and its state will automatically be tracked by the client. Any number of accounts can be created and stored by the client.
The `AccountTemplate` enum defines which type of account will be created. Note that once an account is created, it will be kept locally and its state will automatically be tracked by the client. Any number of accounts can be created and stored by the client. You can also create onchain accounts with: `accounts::AccountStorageMode::OnChain`

```Rust
let account_template = AccountTemplate::BasicWallet {
mutable_code: false,
storage_mode: accounts::AccountStorageMode::OnChain,
};

let (new_account, account_seed) = client.new_account(client_template)?;
```

The account's state is also tracked locally, but during sync the client updates the account state by querying the node for outdated on-chain accounts.

## Execute a transaction

Expand Down
62 changes: 50 additions & 12 deletions src/cli/account.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{fs, path::PathBuf};

use clap::Parser;
use clap::{Parser, ValueEnum};
use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table};
use miden_client::{
client::{accounts, rpc::NodeRpcClient, Client},
Expand Down Expand Up @@ -61,9 +61,15 @@ pub enum AccountCmd {
#[clap()]
pub enum AccountTemplate {
/// Creates a basic account (Regular account with immutable code)
BasicImmutable,
BasicImmutable {
#[clap(short, long, value_enum, default_value_t = AccountStorageMode::OffChain)]
storage_mode: AccountStorageMode,
},
/// Creates a basic account (Regular account with mutable code)
BasicMutable,
BasicMutable {
#[clap(short, long, value_enum, default_value_t = AccountStorageMode::OffChain)]
storage_mode: AccountStorageMode,
},
/// Creates a faucet for fungible tokens
FungibleFaucet {
#[clap(short, long)]
Expand All @@ -72,9 +78,36 @@ pub enum AccountTemplate {
decimals: u8,
#[clap(short, long)]
max_supply: u64,
#[clap(short, long, value_enum, default_value_t = AccountStorageMode::OffChain)]
storage_mode: AccountStorageMode,
},
/// Creates a faucet for non-fungible tokens
NonFungibleFaucet,
NonFungibleFaucet {
#[clap(short, long, value_enum, default_value_t = AccountStorageMode::OffChain)]
storage_mode: AccountStorageMode,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can add this to the AccountCmd::New since it's common to all variants.

},
}

// TODO: Review this enum and variant names to have a consistent naming across all crates
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum AccountStorageMode {
OffChain,
OnChain,
}

impl From<AccountStorageMode> for accounts::AccountStorageMode {
fn from(value: AccountStorageMode) -> Self {
match value {
AccountStorageMode::OffChain => accounts::AccountStorageMode::Local,
AccountStorageMode::OnChain => accounts::AccountStorageMode::OnChain,
}
}
}

impl From<&AccountStorageMode> for accounts::AccountStorageMode {
fn from(value: &AccountStorageMode) -> Self {
accounts::AccountStorageMode::from(*value)
}
Comment on lines +93 to +110
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a TODO to review these struct's names (and maybe the local vs offchain terms)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes! added it here and in the client/accounts.rs one

}

impl AccountCmd {
Expand All @@ -88,26 +121,31 @@ impl AccountCmd {
},
AccountCmd::New { template } => {
let client_template = match template {
AccountTemplate::BasicImmutable => accounts::AccountTemplate::BasicWallet {
mutable_code: false,
storage_mode: accounts::AccountStorageMode::Local,
AccountTemplate::BasicImmutable { storage_mode } => {
accounts::AccountTemplate::BasicWallet {
mutable_code: false,
storage_mode: storage_mode.into(),
}
},
AccountTemplate::BasicMutable => accounts::AccountTemplate::BasicWallet {
mutable_code: true,
storage_mode: accounts::AccountStorageMode::Local,
AccountTemplate::BasicMutable { storage_mode } => {
accounts::AccountTemplate::BasicWallet {
mutable_code: true,
storage_mode: storage_mode.into(),
}
},
AccountTemplate::FungibleFaucet {
token_symbol,
decimals,
max_supply,
storage_mode,
} => accounts::AccountTemplate::FungibleFaucet {
token_symbol: TokenSymbol::new(token_symbol)
.map_err(|err| format!("error: token symbol is invalid: {}", err))?,
decimals: *decimals,
max_supply: *max_supply,
storage_mode: accounts::AccountStorageMode::Local,
storage_mode: storage_mode.into(),
},
AccountTemplate::NonFungibleFaucet => todo!(),
AccountTemplate::NonFungibleFaucet { storage_mode: _ } => todo!(),
};
let (_new_account, _account_seed) = client.new_account(client_template)?;
},
Expand Down
30 changes: 14 additions & 16 deletions src/client/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,22 @@ pub enum AccountTemplate {
},
}

// TODO: Review this enum and variant names to have a consistent naming across all crates
#[derive(Debug, Clone, Copy)]
pub enum AccountStorageMode {
Local,
OnChain,
}

impl From<AccountStorageMode> for AccountStorageType {
fn from(mode: AccountStorageMode) -> Self {
match mode {
AccountStorageMode::Local => AccountStorageType::OffChain,
AccountStorageMode::OnChain => AccountStorageType::OnChain,
}
}
}

impl<N: NodeRpcClient, R: FeltRng, S: Store> Client<N, R, S> {
// ACCOUNT CREATION
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -108,20 +119,11 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store> Client<N, R, S> {
}

/// Creates a new regular account and saves it in the store along with its seed and auth data
///
/// # Panics
///
/// If the passed [AccountStorageMode] is [AccountStorageMode::OnChain], this function panics
/// since this feature is not currently supported on Miden
fn new_basic_wallet(
&mut self,
mutable_code: bool,
account_storage_mode: AccountStorageMode,
) -> Result<(Account, Word), ClientError> {
if let AccountStorageMode::OnChain = account_storage_mode {
todo!("Recording the account on chain is not supported yet");
}

// TODO: This should be initialized with_rng
let key_pair = SecretKey::new();

Expand All @@ -138,14 +140,14 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store> Client<N, R, S> {
init_seed,
auth_scheme,
AccountType::RegularAccountImmutableCode,
AccountStorageType::OffChain,
account_storage_mode.into(),
)
} else {
miden_lib::accounts::wallets::create_basic_wallet(
init_seed,
auth_scheme,
AccountType::RegularAccountUpdatableCode,
AccountStorageType::OffChain,
account_storage_mode.into(),
)
}?;

Expand All @@ -160,10 +162,6 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store> Client<N, R, S> {
max_supply: u64,
account_storage_mode: AccountStorageMode,
) -> Result<(Account, Word), ClientError> {
if let AccountStorageMode::OnChain = account_storage_mode {
todo!("On-chain accounts are not supported yet");
}

// TODO: This should be initialized with_rng
let key_pair = SecretKey::new();

Expand All @@ -181,7 +179,7 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store> Client<N, R, S> {
decimals,
Felt::try_from(max_supply.to_le_bytes().as_slice())
.expect("u64 can be safely converted to a field element"),
AccountStorageType::OffChain,
account_storage_mode.into(),
auth_scheme,
)?;

Expand Down
9 changes: 8 additions & 1 deletion src/client/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::fmt;

use async_trait::async_trait;
use miden_objects::{
accounts::AccountId,
accounts::{Account, AccountId},
crypto::merkle::{MerklePath, MmrDelta},
notes::{NoteId, NoteMetadata},
transaction::ProvenTransaction,
Expand Down Expand Up @@ -56,6 +56,11 @@ pub trait NodeRpcClient {
note_tags: &[u16],
nullifiers_tags: &[u16],
) -> Result<StateSyncInfo, NodeRpcClientError>;

async fn get_account_update(
&mut self,
account_id: AccountId,
) -> Result<Account, NodeRpcClientError>;
}

// STATE SYNC INFO
Expand Down Expand Up @@ -130,6 +135,7 @@ impl CommittedNote {
//
#[derive(Debug)]
pub enum NodeRpcClientEndpoint {
GetAccountDetails,
GetBlockHeaderByNumber,
SyncState,
SubmitProvenTx,
Expand All @@ -141,6 +147,7 @@ impl fmt::Display for NodeRpcClientEndpoint {
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self {
NodeRpcClientEndpoint::GetAccountDetails => write!(f, "get_account_details"),
NodeRpcClientEndpoint::GetBlockHeaderByNumber => {
write!(f, "get_block_header_by_number")
},
Expand Down
45 changes: 43 additions & 2 deletions src/client/rpc/tonic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ use miden_node_proto::{
errors::ConversionError,
generated::{
requests::{
GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, SyncStateRequest,
GetAccountDetailsRequest, GetBlockHeaderByNumberRequest,
SubmitProvenTransactionRequest, SyncStateRequest,
},
responses::SyncStateResponse,
rpc::api_client::ApiClient,
},
};
use miden_objects::{
accounts::AccountId,
accounts::{Account, AccountId},
notes::{NoteId, NoteMetadata, NoteType},
transaction::ProvenTransaction,
utils::Deserializable,
BlockHeader, Digest,
};
use miden_tx::utils::Serializable;
Expand Down Expand Up @@ -127,6 +129,45 @@ impl NodeRpcClient for TonicRpcClient {
})?;
response.into_inner().try_into()
}

/// TODO: fill description
async fn get_account_update(
&mut self,
account_id: AccountId,
) -> Result<Account, NodeRpcClientError> {
if !account_id.is_on_chain() {
return Err(NodeRpcClientError::InvalidAccountReceived(
"should only get updates for offchain accounts".to_string(),
));
}

let account_id = account_id.into();
let request = GetAccountDetailsRequest {
account_id: Some(account_id),
};

let rpc_api = self.rpc_api().await?;

let response = rpc_api.get_account_details(request).await.map_err(|err| {
NodeRpcClientError::RequestError(
NodeRpcClientEndpoint::GetAccountDetails.to_string(),
err.to_string(),
)
})?;
let response = response.into_inner();
let account_info = response.account.ok_or(NodeRpcClientError::ExpectedFieldMissing(
"GetAccountDetails response should have an `account`".to_string(),
))?;

let details_bytes =
account_info.details.ok_or(NodeRpcClientError::ExpectedFieldMissing(
"GetAccountDetails response's account should have `details`".to_string(),
))?;

let details = Account::read_from_bytes(&details_bytes)?;

Ok(details)
}
}

// STATE SYNC INFO CONVERSION
Expand Down
Loading
Loading