Skip to content

Commit 0042e16

Browse files
authored
Merge pull request #1119 from 0xMimir/feat/graphql-pooledCommands
Added `pooledUserCommands` and `pooledZkappCommands` graphql endpoint
2 parents d8d6496 + 9c24619 commit 0042e16

File tree

14 files changed

+403
-90
lines changed

14 files changed

+403
-90
lines changed

node/common/src/service/rpc/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use node::rpc::{
1212
RpcDiscoveryBoostrapStatsResponse, RpcDiscoveryRoutingTableResponse, RpcGetBlockResponse,
1313
RpcHealthCheckResponse, RpcHeartbeatGetResponse, RpcLedgerAccountsResponse,
1414
RpcLedgerSlimAccountsResponse, RpcMessageProgressResponse, RpcPeersGetResponse,
15-
RpcReadinessCheckResponse, RpcRequest, RpcStateGetError, RpcStatusGetResponse,
16-
RpcTransactionInjectResponse, RpcTransactionPoolResponse, RpcTransactionStatusGetResponse,
15+
RpcPooledUserCommandsResponse, RpcPooledZkappCommandsResponse, RpcReadinessCheckResponse,
16+
RpcRequest, RpcStateGetError, RpcStatusGetResponse, RpcTransactionInjectResponse,
17+
RpcTransactionPoolResponse, RpcTransactionStatusGetResponse,
1718
RpcTransitionFrontierUserCommandsResponse,
1819
};
1920
use serde::{Deserialize, Serialize};
@@ -309,6 +310,11 @@ impl node::rpc_effectful::RpcService for NodeService {
309310
);
310311
rpc_service_impl!(respond_transaction_status, RpcTransactionStatusGetResponse);
311312
rpc_service_impl!(respond_block_get, RpcGetBlockResponse);
313+
rpc_service_impl!(respond_pooled_user_commands, RpcPooledUserCommandsResponse);
314+
rpc_service_impl!(
315+
respond_pooled_zkapp_commands,
316+
RpcPooledZkappCommandsResponse
317+
);
312318
}
313319

314320
#[cfg(test)]

node/native/src/graphql/block.rs

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::graphql::zkapp::{GraphQLFailureReason, GraphQLFeePayer, GraphQLZkappCommand};
22
use juniper::{GraphQLEnum, GraphQLObject};
33
use mina_p2p_messages::v2::{
4-
MinaBaseSignedCommandPayloadBodyStableV2, MinaBaseStakeDelegationStableV2,
5-
TransactionSnarkWorkTStableV2,
4+
MinaBaseSignedCommandPayloadBodyStableV2, MinaBaseSignedCommandStableV2,
5+
MinaBaseStakeDelegationStableV2, TransactionSnarkWorkTStableV2,
66
};
77
use openmina_core::block::AppliedBlock;
88

@@ -203,48 +203,7 @@ impl TryFrom<mina_p2p_messages::v2::StagedLedgerDiffDiffDiffStableV2> for GraphQ
203203
for command in commands {
204204
match command.data {
205205
MinaBaseUserCommandStableV2::SignedCommand(user_command) => {
206-
let is_delegation = matches!(
207-
user_command.payload.body,
208-
MinaBaseSignedCommandPayloadBodyStableV2::StakeDelegation(_)
209-
);
210-
let hash = user_command.hash()?.to_string();
211-
212-
let fee = user_command.payload.common.fee.to_string();
213-
let memo = user_command.payload.common.memo.to_base58check();
214-
let nonce = user_command.payload.common.nonce.as_u32() as i32;
215-
let valid_until = user_command.payload.common.valid_until.as_u32().to_string();
216-
217-
let (to, amount, kind) = match user_command.payload.body {
218-
MinaBaseSignedCommandPayloadBodyStableV2::Payment(payment) => (
219-
payment.receiver_pk.to_string(),
220-
Some(payment.amount.to_string()),
221-
GraphQLUserCommandsKind::PAYMENT,
222-
),
223-
MinaBaseSignedCommandPayloadBodyStableV2::StakeDelegation(
224-
MinaBaseStakeDelegationStableV2::SetDelegate { new_delegate },
225-
) => (
226-
new_delegate.to_string(),
227-
None,
228-
GraphQLUserCommandsKind::STAKE_DELEGATION,
229-
),
230-
};
231-
232-
user_commands.push(GraphQLUserCommands {
233-
hash,
234-
from: user_command.signer.to_string(),
235-
to,
236-
is_delegation,
237-
amount,
238-
failure_reason: Default::default(),
239-
fee,
240-
fee_token: Default::default(),
241-
id: Default::default(),
242-
kind,
243-
memo,
244-
nonce,
245-
token: Default::default(),
246-
valid_until,
247-
});
206+
user_commands.push(GraphQLUserCommands::try_from(user_command)?);
248207
}
249208
MinaBaseUserCommandStableV2::ZkappCommand(zkapp) => {
250209
let failure_reason =
@@ -377,3 +336,53 @@ impl From<&TransactionSnarkWorkTStableV2> for GraphQLSnarkJob {
377336
}
378337
}
379338
}
339+
340+
impl TryFrom<MinaBaseSignedCommandStableV2> for GraphQLUserCommands {
341+
type Error = ConversionError;
342+
343+
fn try_from(user_command: MinaBaseSignedCommandStableV2) -> Result<Self, Self::Error> {
344+
let is_delegation = matches!(
345+
user_command.payload.body,
346+
MinaBaseSignedCommandPayloadBodyStableV2::StakeDelegation(_)
347+
);
348+
let hash = user_command.hash()?.to_string();
349+
let id = user_command.to_base64()?;
350+
351+
let fee = user_command.payload.common.fee.to_string();
352+
let memo = user_command.payload.common.memo.to_base58check();
353+
let nonce = user_command.payload.common.nonce.as_u32() as i32;
354+
let valid_until = user_command.payload.common.valid_until.as_u32().to_string();
355+
356+
let (to, amount, kind) = match user_command.payload.body {
357+
MinaBaseSignedCommandPayloadBodyStableV2::Payment(payment) => (
358+
payment.receiver_pk.to_string(),
359+
Some(payment.amount.to_string()),
360+
GraphQLUserCommandsKind::PAYMENT,
361+
),
362+
MinaBaseSignedCommandPayloadBodyStableV2::StakeDelegation(
363+
MinaBaseStakeDelegationStableV2::SetDelegate { new_delegate },
364+
) => (
365+
new_delegate.to_string(),
366+
None,
367+
GraphQLUserCommandsKind::STAKE_DELEGATION,
368+
),
369+
};
370+
371+
Ok(GraphQLUserCommands {
372+
hash,
373+
from: user_command.signer.to_string(),
374+
to,
375+
is_delegation,
376+
amount,
377+
failure_reason: Default::default(),
378+
fee,
379+
fee_token: Default::default(),
380+
id,
381+
kind,
382+
memo,
383+
nonce,
384+
token: Default::default(),
385+
valid_until,
386+
})
387+
}
388+
}

node/native/src/graphql/mod.rs

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
use block::GraphQLBlock;
2-
use juniper::{graphql_value, FieldError};
3-
use juniper::{EmptySubscription, GraphQLEnum, RootNode};
1+
use block::{GraphQLBlock, GraphQLUserCommands};
2+
use juniper::{graphql_value, EmptySubscription, FieldError, GraphQLEnum, RootNode};
43
use ledger::Account;
5-
use mina_p2p_messages::v2::MinaBaseSignedCommandStableV2;
6-
use mina_p2p_messages::v2::MinaBaseUserCommandStableV2;
7-
use mina_p2p_messages::v2::MinaBaseZkappCommandTStableV1WireStableV1;
8-
use mina_p2p_messages::v2::TokenIdKeyHash;
9-
use node::rpc::RpcTransactionInjectResponse;
10-
use node::rpc::{GetBlockQuery, RpcGetBlockResponse, RpcTransactionStatusGetResponse};
4+
use mina_p2p_messages::v2::{
5+
conv, MinaBaseSignedCommandStableV2, MinaBaseUserCommandStableV2,
6+
MinaBaseZkappCommandTStableV1WireStableV1, TokenIdKeyHash, TransactionHash,
7+
};
118
use node::{
129
account::AccountPublicKey,
13-
rpc::{AccountQuery, RpcRequest, RpcSyncStatsGetResponse, SyncStatsQuery},
10+
rpc::{
11+
AccountQuery, GetBlockQuery, PooledCommandsQuery, RpcGetBlockResponse,
12+
RpcPooledUserCommandsResponse, RpcPooledZkappCommandsResponse, RpcRequest,
13+
RpcSyncStatsGetResponse, RpcTransactionInjectResponse, RpcTransactionStatusGetResponse,
14+
SyncStatsQuery,
15+
},
1416
stats::sync::SyncKind,
1517
};
1618
use o1_utils::field_helpers::FieldHelpersError;
17-
use openmina_core::block::AppliedBlock;
18-
use openmina_core::consensus::ConsensusConstants;
19-
use openmina_core::constants::constraint_constants;
19+
use openmina_core::{
20+
block::AppliedBlock, consensus::ConsensusConstants, constants::constraint_constants,
21+
};
2022
use openmina_node_common::rpc::RpcSender;
2123
use std::str::FromStr;
2224
use transaction::GraphQLTransactionStatus;
2325
use warp::{Filter, Rejection, Reply};
26+
use zkapp::GraphQLZkapp;
2427

2528
pub mod account;
2629
pub mod block;
@@ -51,6 +54,8 @@ pub enum ConversionError {
5154
SerdeJson(#[from] serde_json::Error),
5255
#[error("Base58Check: {0}")]
5356
Base58Check(#[from] mina_p2p_messages::b58::FromBase58CheckError),
57+
#[error("Base58 error: {0}")]
58+
Base58(#[from] bs58::decode::Error),
5459
#[error(transparent)]
5560
InvalidDecimalNumber(#[from] mina_p2p_messages::bigint::InvalidDecimalNumber),
5661
#[error("Invalid bigint")]
@@ -319,6 +324,73 @@ impl Query {
319324
Some(Some(block)) => Ok(GraphQLBlock::try_from(block)?),
320325
}
321326
}
327+
328+
/// Retrieve all the scheduled user commands for a specified sender that
329+
/// the current daemon sees in its transaction pool. All scheduled
330+
/// commands are queried if no sender is specified
331+
///
332+
/// Arguments:
333+
/// - `public_key`: base58 encoded [`AccountPublicKey`]
334+
/// - `hashes`: list of base58 encoded [`TransactionHash`]es
335+
/// - `ids`: list of base64 encoded [`MinaBaseZkappCommandTStableV1WireStableV1`]
336+
async fn pooled_user_commands(
337+
&self,
338+
public_key: Option<String>,
339+
hashes: Option<Vec<String>>,
340+
ids: Option<Vec<String>>,
341+
context: &Context,
342+
) -> juniper::FieldResult<Vec<GraphQLUserCommands>> {
343+
let query = parse_pooled_commands_query(
344+
public_key,
345+
hashes,
346+
ids,
347+
MinaBaseSignedCommandStableV2::from_base64,
348+
)?;
349+
350+
let res: RpcPooledUserCommandsResponse = context
351+
.0
352+
.oneshot_request(RpcRequest::PooledUserCommands(query))
353+
.await
354+
.ok_or(Error::StateMachineEmptyResponse)?;
355+
356+
Ok(res
357+
.into_iter()
358+
.map(GraphQLUserCommands::try_from)
359+
.collect::<Result<Vec<_>, _>>()?)
360+
}
361+
362+
/// Retrieve all the scheduled zkApp commands for a specified sender that
363+
/// the current daemon sees in its transaction pool. All scheduled
364+
/// commands are queried if no sender is specified
365+
///
366+
/// Arguments:
367+
/// - `public_key`: base58 encoded [`AccountPublicKey`]
368+
/// - `hashes`: list of base58 encoded [`TransactionHash`]es
369+
/// - `ids`: list of base64 encoded [`MinaBaseZkappCommandTStableV1WireStableV1`]
370+
async fn pooled_zkapp_commands(
371+
public_key: Option<String>,
372+
hashes: Option<Vec<String>>,
373+
ids: Option<Vec<String>>,
374+
context: &Context,
375+
) -> juniper::FieldResult<Vec<GraphQLZkapp>> {
376+
let query = parse_pooled_commands_query(
377+
public_key,
378+
hashes,
379+
ids,
380+
MinaBaseZkappCommandTStableV1WireStableV1::from_base64,
381+
)?;
382+
383+
let res: RpcPooledZkappCommandsResponse = context
384+
.0
385+
.oneshot_request(RpcRequest::PooledZkappCommands(query))
386+
.await
387+
.ok_or(Error::StateMachineEmptyResponse)?;
388+
389+
Ok(res
390+
.into_iter()
391+
.map(GraphQLZkapp::try_from)
392+
.collect::<Result<Vec<_>, _>>()?)
393+
}
322394
}
323395

324396
async fn inject_tx<R>(
@@ -371,6 +443,7 @@ where
371443
}
372444
}
373445
}
446+
374447
#[derive(Clone, Debug)]
375448
struct Mutation;
376449

@@ -488,3 +561,44 @@ pub fn routes(
488561
// )))
489562
// .or(homepage)
490563
// .with(log);
564+
565+
/// Helper function used by [`Query::pooled_user_commands`] and [`Query::pooled_zkapp_commands`] to parse public key, transaction hashes and command ids
566+
fn parse_pooled_commands_query<ID, F>(
567+
public_key: Option<String>,
568+
hashes: Option<Vec<String>>,
569+
ids: Option<Vec<String>>,
570+
id_map_fn: F,
571+
) -> Result<PooledCommandsQuery<ID>, ConversionError>
572+
where
573+
F: Fn(&str) -> Result<ID, conv::Error>,
574+
{
575+
let public_key = match public_key {
576+
Some(public_key) => Some(AccountPublicKey::from_str(&public_key)?),
577+
None => None,
578+
};
579+
580+
let hashes = match hashes {
581+
Some(hashes) => Some(
582+
hashes
583+
.into_iter()
584+
.map(|tx| TransactionHash::from_str(tx.as_str()))
585+
.collect::<Result<Vec<_>, _>>()?,
586+
),
587+
None => None,
588+
};
589+
590+
let ids = match ids {
591+
Some(ids) => Some(
592+
ids.into_iter()
593+
.map(|id| id_map_fn(id.as_str()))
594+
.collect::<Result<Vec<_>, _>>()?,
595+
),
596+
None => None,
597+
};
598+
599+
Ok(PooledCommandsQuery {
600+
public_key,
601+
hashes,
602+
ids,
603+
})
604+
}

node/native/src/graphql/zkapp.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -103,29 +103,37 @@ pub struct InputGraphQLZkappCommand {
103103
pub fee_payer: InputGraphQLFeePayer,
104104
}
105105

106+
impl TryFrom<MinaBaseZkappCommandTStableV1WireStableV1> for GraphQLZkapp {
107+
type Error = ConversionError;
108+
109+
fn try_from(zkapp: MinaBaseZkappCommandTStableV1WireStableV1) -> Result<Self, Self::Error> {
110+
let account_updates = zkapp
111+
.account_updates
112+
.clone()
113+
.into_iter()
114+
.map(|v| v.elt.account_update.try_into())
115+
.collect::<Result<Vec<_>, _>>()?;
116+
117+
Ok(GraphQLZkapp {
118+
hash: zkapp.hash()?.to_string(),
119+
failure_reason: None,
120+
id: zkapp.to_base64()?,
121+
zkapp_command: GraphQLZkappCommand {
122+
memo: zkapp.memo.to_base58check(),
123+
account_updates,
124+
fee_payer: GraphQLFeePayer::from(zkapp.fee_payer),
125+
},
126+
})
127+
}
128+
}
129+
106130
impl TryFrom<MinaBaseUserCommandStableV2> for GraphQLSendZkappResponse {
107131
type Error = ConversionError;
108132
fn try_from(value: MinaBaseUserCommandStableV2) -> Result<Self, Self::Error> {
109133
if let MinaBaseUserCommandStableV2::ZkappCommand(zkapp) = value {
110-
let account_updates = zkapp
111-
.account_updates
112-
.clone()
113-
.into_iter()
114-
.map(|v| v.elt.account_update.try_into())
115-
.collect::<Result<Vec<_>, _>>()?;
116-
let res = GraphQLSendZkappResponse {
117-
zkapp: GraphQLZkapp {
118-
hash: zkapp.hash()?.to_string(),
119-
failure_reason: None,
120-
id: zkapp.to_base64()?,
121-
zkapp_command: GraphQLZkappCommand {
122-
memo: zkapp.memo.to_base58check(),
123-
account_updates,
124-
fee_payer: GraphQLFeePayer::from(zkapp.fee_payer),
125-
},
126-
},
127-
};
128-
Ok(res)
134+
Ok(GraphQLSendZkappResponse {
135+
zkapp: GraphQLZkapp::try_from(zkapp)?,
136+
})
129137
} else {
130138
Err(ConversionError::WrongVariant)
131139
}

0 commit comments

Comments
 (0)