Skip to content

Commit d542f9e

Browse files
author
Adrian Nagy
committed
WIP: account loader implementation
1 parent b272738 commit d542f9e

File tree

7 files changed

+158
-54
lines changed

7 files changed

+158
-54
lines changed

ledger/src/account/account.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,10 @@ impl AccountId {
10791079
}
10801080
}
10811081

1082+
pub fn new_with_default_token(public_key: CompressedPubKey) -> Self {
1083+
Self::new(public_key, TokenId::default())
1084+
}
1085+
10821086
pub fn create(public_key: CompressedPubKey, token_id: TokenId) -> Self {
10831087
Self::new(public_key, token_id)
10841088
}

node/native/src/graphql/account.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,43 @@ use std::{collections::HashMap, sync::Arc};
22

33
use dataloader::non_cached::Loader;
44
use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject};
5-
use ledger::{AccountId, FpExt};
5+
use ledger::{Account, AccountId, FpExt};
66
use mina_p2p_messages::{
77
string::{TokenSymbol, ZkAppUri},
88
v2::{
99
MinaBaseAccountUpdateUpdateTimingInfoStableV1, MinaBaseVerificationKeyWireStableV1,
1010
ReceiptChainHash, TokenIdKeyHash,
1111
},
1212
};
13-
use node::account::AccountPublicKey;
13+
use mina_signer::CompressedPubKey;
14+
use node::rpc::{AccountQuery, RpcRequest};
1415
use openmina_node_common::rpc::RpcSender;
1516

1617
use super::{Context, ConversionError};
1718

1819
pub(crate) type AccountLoader =
19-
Loader<AccountPublicKey, Result<GraphQLAccount, Arc<ConversionError>>, AccountBatcher>;
20+
Loader<AccountId, Result<GraphQLAccount, Arc<ConversionError>>, AccountBatcher>;
2021

2122
pub(crate) struct AccountBatcher {
2223
rpc_sender: RpcSender,
2324
}
2425

25-
impl dataloader::BatchFn<AccountPublicKey, Result<GraphQLAccount, Arc<ConversionError>>>
26+
impl dataloader::BatchFn<AccountId, Result<GraphQLAccount, Arc<ConversionError>>>
2627
for AccountBatcher
2728
{
2829
async fn load(
2930
&mut self,
30-
keys: &[AccountPublicKey],
31-
) -> HashMap<AccountPublicKey, Result<GraphQLAccount, Arc<ConversionError>>> {
32-
todo!()
31+
keys: &[AccountId],
32+
) -> HashMap<AccountId, Result<GraphQLAccount, Arc<ConversionError>>> {
33+
self.rpc_sender
34+
.oneshot_request::<Vec<Account>>(RpcRequest::LedgerAccountsGet(
35+
AccountQuery::MultipleIds(keys.to_vec()),
36+
))
37+
.await
38+
.unwrap_or_default()
39+
.into_iter()
40+
.map(|account| (account.id(), account.try_into().map_err(Arc::new)))
41+
.collect()
3342
}
3443
}
3544

@@ -47,9 +56,8 @@ pub(crate) struct GraphQLAccount {
4756
balance: GraphQLBalance,
4857
nonce: String,
4958
receipt_chain_hash: String,
50-
delegate_account: Option<Box<GraphQLAccount>>,
5159
// Storing the key for later
52-
delegate_key: Option<AccountPublicKey>,
60+
delegate_key: Option<CompressedPubKey>,
5361
voting_for: String,
5462
timing: GraphQLTiming,
5563
permissions: GraphQLPermissions,
@@ -99,17 +107,20 @@ impl GraphQLAccount {
99107
) -> FieldResult<Option<Box<GraphQLAccount>>> {
100108
// If we have a delegate key
101109
if let Some(delegate_key) = self.delegate_key.as_ref() {
110+
// A delegate always has the default token id
111+
let delegate_id = AccountId::new_with_default_token(delegate_key.clone());
102112
// Use the loader to fetch the delegate account
103-
let delegate_result = context
104-
.account_loader
105-
.try_load(delegate_key.clone())
106-
.await
107-
.map_err(|e| {
108-
juniper::FieldError::new(
109-
format!("Failed to load delegate account: {}", e),
110-
juniper::Value::null(),
111-
)
112-
})?;
113+
let delegate_result =
114+
context
115+
.account_loader
116+
.try_load(delegate_id)
117+
.await
118+
.map_err(|e| {
119+
juniper::FieldError::new(
120+
format!("Failed to load delegate account: {}", e),
121+
juniper::Value::null(),
122+
)
123+
})?;
113124

114125
// Handle the result
115126
match delegate_result {
@@ -243,12 +254,6 @@ pub struct GraphQLVerificationKey {
243254
pub hash: String,
244255
}
245256

246-
#[derive(GraphQLObject, Debug, Clone)]
247-
/// Dummy type to represent [`GraphQLAccount`]
248-
pub struct GraphQLDummyAccount {
249-
pub public_key: String,
250-
}
251-
252257
impl From<ledger::SetVerificationKey<ledger::AuthRequired>> for GraphQLSetVerificationKey {
253258
fn from(value: ledger::SetVerificationKey<ledger::AuthRequired>) -> Self {
254259
Self {
@@ -342,9 +347,7 @@ impl TryFrom<ledger::Account> for GraphQLAccount {
342347
balance: GraphQLBalance::from(value.balance),
343348
nonce: value.nonce.as_u32().to_string(),
344349
receipt_chain_hash: ReceiptChainHash::from(value.receipt_chain_hash).to_string(),
345-
delegate_key: value.delegate.map(AccountPublicKey::from),
346-
// Initialy set to None, will be set in the resolver
347-
delegate_account: None,
350+
delegate_key: value.delegate,
348351
voting_for: value.voting_for.to_base58check_graphql(),
349352
timing: GraphQLTiming::from(value.timing),
350353
permissions: GraphQLPermissions::from(value.permissions),

node/native/src/graphql/block.rs

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,119 @@
1-
use crate::graphql::zkapp::{GraphQLFailureReason, GraphQLFeePayer, GraphQLZkappCommand};
2-
use juniper::{GraphQLEnum, GraphQLObject};
1+
use crate::graphql::{
2+
account::GraphQLAccount,
3+
zkapp::{GraphQLFailureReason, GraphQLFeePayer, GraphQLZkappCommand},
4+
};
5+
use juniper::{graphql_object, FieldResult, GraphQLEnum, GraphQLObject};
6+
use ledger::AccountId;
37
use mina_p2p_messages::v2::{
48
MinaBaseSignedCommandPayloadBodyStableV2, MinaBaseSignedCommandStableV2,
59
MinaBaseStakeDelegationStableV2, TransactionSnarkWorkTStableV2,
610
};
11+
use mina_signer::CompressedPubKey;
12+
use node::account::AccountPublicKey;
713
use openmina_core::block::AppliedBlock;
814

9-
use super::{account::GraphQLDummyAccount, zkapp::GraphQLZkapp, ConversionError};
15+
use super::{zkapp::GraphQLZkapp, Context, ConversionError};
1016

11-
#[derive(GraphQLObject, Debug)]
12-
#[graphql(description = "A Mina block")]
17+
#[derive(Debug)]
1318
/// Location [src/lib/mina_graphql/types.ml:2095](https://github.com/MinaProtocol/mina/blob/develop/src/lib/mina_graphql/types.ml#L2095-L2151)
14-
pub struct GraphQLBlock {
15-
pub creator: String,
16-
/// TODO: this must be fetched separately from `AppliedBlock`
17-
pub creator_account: GraphQLDummyAccount,
18-
/// TODO: this must be fetched separately from `AppliedBlock`
19-
pub winner_account: GraphQLDummyAccount,
20-
pub state_hash: String,
19+
pub(crate) struct GraphQLBlock {
20+
creator: String,
21+
creator_account_key: CompressedPubKey,
22+
winner_account_key: CompressedPubKey,
23+
state_hash: String,
2124
/// Experimental: Bigint field-element representation of stateHash
22-
pub state_hash_field: String,
23-
pub protocol_state: GraphQLProtocolState,
25+
state_hash_field: String,
26+
protocol_state: GraphQLProtocolState,
2427
/// Public key of account that produced this block
2528
/// use creatorAccount field instead
26-
pub transactions: GraphQLTransactions,
29+
transactions: GraphQLTransactions,
2730
/// Base58Check-encoded hash of the state after this block
2831
/// Count of user command transactions in the block
29-
pub command_transaction_count: i32,
30-
pub snark_jobs: Vec<GraphQLSnarkJob>,
32+
command_transaction_count: i32,
33+
snark_jobs: Vec<GraphQLSnarkJob>,
34+
}
35+
36+
#[graphql_object(context = Context)]
37+
#[graphql(description = "A Mina block")]
38+
impl GraphQLBlock {
39+
fn creator(&self) -> &str {
40+
&self.creator
41+
}
42+
43+
async fn creator_account(&self, context: &Context) -> FieldResult<Box<GraphQLAccount>> {
44+
// TODO(adonagy): cleanup
45+
let account_id = AccountId::new_with_default_token(self.creator_account_key.clone());
46+
// Use the loader to fetch the delegate account
47+
let account_result = context
48+
.account_loader
49+
.try_load(account_id)
50+
.await
51+
.map_err(|e| {
52+
juniper::FieldError::new(
53+
format!("Failed to load delegate account: {}", e),
54+
juniper::Value::null(),
55+
)
56+
})?;
57+
58+
// Handle the result
59+
match account_result {
60+
Ok(account) => Ok(Box::new(account)),
61+
Err(e) => Err(juniper::FieldError::new(
62+
format!("Error loading delegate account: {}", e),
63+
juniper::Value::null(),
64+
)),
65+
}
66+
}
67+
68+
async fn winner_account(&self, context: &Context) -> FieldResult<Box<GraphQLAccount>> {
69+
// TODO(adonagy): cleanup
70+
let account_id = AccountId::new_with_default_token(self.winner_account_key.clone());
71+
// Use the loader to fetch the delegate account
72+
let account_result = context
73+
.account_loader
74+
.try_load(account_id)
75+
.await
76+
.map_err(|e| {
77+
juniper::FieldError::new(
78+
format!("Failed to load delegate account: {}", e),
79+
juniper::Value::null(),
80+
)
81+
})?;
82+
83+
// Handle the result
84+
match account_result {
85+
Ok(account) => Ok(Box::new(account)),
86+
Err(e) => Err(juniper::FieldError::new(
87+
format!("Error loading delegate account: {}", e),
88+
juniper::Value::null(),
89+
)),
90+
}
91+
}
92+
93+
async fn state_hash(&self) -> &str {
94+
&self.state_hash
95+
}
96+
97+
/// Experimental: Bigint field-element representation of stateHash
98+
async fn state_hash_field(&self) -> &str {
99+
&self.state_hash_field
100+
}
101+
102+
async fn protocol_state(&self) -> &GraphQLProtocolState {
103+
&self.protocol_state
104+
}
105+
106+
async fn transactions(&self) -> &GraphQLTransactions {
107+
&self.transactions
108+
}
109+
110+
async fn command_transaction_count(&self) -> i32 {
111+
self.command_transaction_count
112+
}
113+
114+
async fn snark_jobs(&self) -> &Vec<GraphQLSnarkJob> {
115+
&self.snark_jobs
116+
}
31117
}
32118

33119
#[derive(GraphQLObject, Debug)]
@@ -116,12 +202,12 @@ impl TryFrom<AppliedBlock> for GraphQLBlock {
116202
.collect();
117203

118204
Ok(Self {
119-
creator_account: GraphQLDummyAccount {
120-
public_key: block.producer().to_string(),
121-
},
122-
winner_account: GraphQLDummyAccount {
123-
public_key: block.block_stake_winner().to_string(),
124-
},
205+
creator_account_key: AccountPublicKey::from(block.producer().clone())
206+
.try_into()
207+
.map_err(|_| ConversionError::Custom("Invalid public key".to_string()))?,
208+
winner_account_key: AccountPublicKey::from(block.block_stake_winner().clone())
209+
.try_into()
210+
.map_err(|_| ConversionError::Custom("Invalid public key".to_string()))?,
125211
protocol_state,
126212
state_hash: block.hash.to_string(),
127213
state_hash_field: block.hash.to_decimal(),

node/native/src/graphql/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,11 @@ impl From<ConversionError> for Error {
104104
/// This optimizes the number of request to the state machine
105105
pub(crate) struct Context {
106106
rpc_sender: RpcSender,
107+
account_loader: AccountLoader,
108+
// Caches
107109
statemachine_status_cache: OnceCell<Option<RpcNodeStatus>>,
108110
best_tip_cache: OnceCell<Option<AppliedBlock>>,
109111
ledger_status_cache: OnceCell<Option<LedgerStatus>>,
110-
account_loader: AccountLoader,
111112
}
112113

113114
impl juniper::Context for Context {}
@@ -119,7 +120,7 @@ impl Context {
119120
statemachine_status_cache: OnceCell::new(),
120121
best_tip_cache: OnceCell::new(),
121122
ledger_status_cache: OnceCell::new(),
122-
account_loader: create_account_loader(rpc_sender),
123+
account_loader: create_account_loader(rpc_sender.clone()),
123124
}
124125
}
125126

node/src/ledger/ledger_manager.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ impl LedgerRequest {
246246
};
247247
ledger_ctx.get_accounts(ledger_hash, vec![id])
248248
}
249+
AccountQuery::MultipleIds(ids) => {
250+
ledger_ctx.get_accounts(ledger_hash, ids.clone())
251+
}
249252
};
250253

251254
LedgerReadResponse::AccountsForRpc(rpc_id, res, account_query)

node/src/rpc/rpc_actions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use ledger::transaction_pool::{diff, ValidCommandWithHash};
2-
use ledger::Account;
2+
use ledger::{Account, AccountId};
33
use mina_p2p_messages::v2::TokenIdKeyHash;
44
use mina_p2p_messages::v2::{LedgerHash, MinaBaseUserCommandStableV2};
55
use openmina_core::block::AppliedBlock;
@@ -253,6 +253,7 @@ pub enum RpcAction {
253253
pub enum AccountQuery {
254254
All,
255255
SinglePublicKey(AccountPublicKey),
256+
MultipleIds(Vec<AccountId>),
256257
PubKeyWithTokenId(AccountPublicKey, TokenIdKeyHash),
257258
}
258259

node/src/rpc_effectful/rpc_effectful_effects.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,12 @@ pub fn rpc_effects<S: Service>(store: &mut Store<S>, action: ActionWithMeta<RpcE
689689
meta.time()
690690
)
691691
}
692+
AccountQuery::MultipleIds(..) => {
693+
respond_or_log!(
694+
store.service().respond_ledger_accounts(rpc_id, accounts),
695+
meta.time()
696+
)
697+
}
692698
}
693699
}
694700
RpcEffectfulAction::TransactionInjectSuccess { rpc_id, response } => {

0 commit comments

Comments
 (0)