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

jsonrpc: allow parse object at tx status requests #9648

Merged
merged 1 commit into from
Oct 12, 2023
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 chain/jsonrpc-primitives/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use near_primitives::errors::TxExecutionError;
use serde_json::{to_value, Value};
use std::fmt;

#[derive(serde::Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct RpcParseError(pub String);

/// This struct may be returned from JSON RPC server in case of error
Expand Down
31 changes: 27 additions & 4 deletions chain/jsonrpc-primitives/src/types/transactions.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
use near_primitives::hash::CryptoHash;
use near_primitives::types::AccountId;
use serde_json::Value;

#[derive(Debug, Clone)]
pub struct RpcBroadcastTransactionRequest {
pub signed_transaction: near_primitives::transaction::SignedTransaction,
}

#[derive(Debug)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RpcTransactionStatusCommonRequest {
#[serde(flatten)]
telezhnaya marked this conversation as resolved.
Show resolved Hide resolved
pub transaction_info: TransactionInfo,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
telezhnaya marked this conversation as resolved.
Show resolved Hide resolved
pub enum TransactionInfo {
#[serde(skip_deserializing)]
Transaction(near_primitives::transaction::SignedTransaction),
TransactionId {
hash: near_primitives::hash::CryptoHash,
account_id: near_primitives::types::AccountId,
tx_hash: CryptoHash,
sender_account_id: AccountId,
},
}

Expand Down Expand Up @@ -51,6 +56,24 @@ pub struct RpcBroadcastTxSyncResponse {
pub transaction_hash: near_primitives::hash::CryptoHash,
}

impl From<TransactionInfo> for RpcTransactionStatusCommonRequest {
fn from(transaction_info: TransactionInfo) -> Self {
Self { transaction_info }
}
}

impl From<near_primitives::transaction::SignedTransaction> for RpcTransactionStatusCommonRequest {
fn from(transaction_info: near_primitives::transaction::SignedTransaction) -> Self {
Self { transaction_info: transaction_info.into() }
}
}

impl From<near_primitives::transaction::SignedTransaction> for TransactionInfo {
fn from(transaction_info: near_primitives::transaction::SignedTransaction) -> Self {
Self::Transaction(transaction_info)
}
}

impl From<RpcTransactionError> for crate::errors::RpcError {
fn from(error: RpcTransactionError) -> Self {
let error_data = match &error {
Expand Down
13 changes: 6 additions & 7 deletions chain/jsonrpc/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,12 @@ mod params {
self.0.unwrap_or_else(Self::parse)
}

/// If value hasn’t been parsed yet, tries to deserialise it directly
/// into `T` using given parse function.
pub fn unwrap_or_else(
self,
func: impl FnOnce(Value) -> Result<T, RpcParseError>,
) -> Result<T, RpcParseError> {
self.0.unwrap_or_else(func)
/// Finish chain of parsing without trying of parsing to `T` directly
pub fn unwrap(self) -> Result<T, RpcParseError> {
match self.0 {
Ok(res) => res,
Err(e) => Err(RpcParseError(format!("Failed parsing args: {e}"))),
}
}

/// If value hasn’t been parsed yet and it’s a one-element array
Expand Down
34 changes: 19 additions & 15 deletions chain/jsonrpc/src/api/transactions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use serde_json::Value;
use serde_with::base64::Base64;
use serde_with::serde_as;

use near_client_primitives::types::TxStatusError;
use near_jsonrpc_primitives::errors::RpcParseError;
Expand All @@ -15,19 +13,20 @@ use super::{Params, RpcFrom, RpcRequest};

impl RpcRequest for RpcBroadcastTransactionRequest {
fn parse(value: Value) -> Result<Self, RpcParseError> {
let signed_transaction = decode_signed_transaction(value)?;
let signed_transaction =
Params::new(value).try_singleton(|value| decode_signed_transaction(value)).unwrap()?;
Ok(Self { signed_transaction })
}
}

impl RpcRequest for RpcTransactionStatusCommonRequest {
fn parse(value: Value) -> Result<Self, RpcParseError> {
let transaction_info = Params::<TransactionInfo>::new(value)
.try_pair(|hash, account_id| Ok(TransactionInfo::TransactionId { hash, account_id }))
.unwrap_or_else(|value| {
decode_signed_transaction(value).map(TransactionInfo::Transaction)
})?;
Ok(Self { transaction_info })
Ok(Params::new(value)
.try_singleton(|signed_tx| decode_signed_transaction(signed_tx).map(|x| x.into()))
.try_pair(|tx_hash, sender_account_id| {
Ok(TransactionInfo::TransactionId { tx_hash, sender_account_id }.into())
})
.unwrap_or_parse()?)
}
}

Expand All @@ -52,12 +51,9 @@ impl RpcFrom<TxStatusError> for RpcTransactionError {
}
}

fn decode_signed_transaction(value: Value) -> Result<SignedTransaction, RpcParseError> {
#[serde_as]
#[derive(serde::Deserialize)]
struct Payload(#[serde_as(as = "(Base64,)")] (Vec<u8>,));

let Payload((bytes,)) = Params::<Payload>::parse(value)?;
fn decode_signed_transaction(value: String) -> Result<SignedTransaction, RpcParseError> {
let bytes = near_primitives::serialize::from_base64(&value)
.map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))?;
SignedTransaction::try_from_slice(&bytes)
.map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))
}
Expand All @@ -81,6 +77,14 @@ mod tests {
assert!(RpcTransactionStatusCommonRequest::parse(params).is_ok());
}

#[test]
fn test_serialize_tx_status_params_as_object() {
let tx_hash = CryptoHash::new().to_string();
let account_id = "sender.testnet";
let params = serde_json::json!({"tx_hash": tx_hash, "sender_account_id": account_id});
assert!(RpcTransactionStatusCommonRequest::parse(params).is_ok());
}

#[test]
fn test_serialize_tx_status_params_as_binary_signed_tx() {
let tx_hash = CryptoHash::new();
Expand Down
17 changes: 5 additions & 12 deletions chain/jsonrpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,9 @@ impl JsonRpcHandler {
(tx.get_hash(), tx.transaction.signer_id.clone())
}
near_jsonrpc_primitives::types::transactions::TransactionInfo::TransactionId {
hash,
account_id,
} => (*hash, account_id.clone()),
tx_hash,
sender_account_id,
} => (*tx_hash, sender_account_id.clone()),
};
timeout(self.polling_config.polling_timeout, async {
loop {
Expand Down Expand Up @@ -703,14 +703,7 @@ impl JsonRpcHandler {
near_jsonrpc_primitives::types::transactions::RpcTransactionError,
> {
let tx = request_data.signed_transaction;
match self
.tx_status_fetch(
near_jsonrpc_primitives::types::transactions::TransactionInfo::Transaction(
tx.clone(),
),
false,
)
.await
match self.tx_status_fetch(tx.clone().into(), false).await
{
Ok(outcome) => {
return Ok(outcome);
Expand All @@ -724,7 +717,7 @@ impl JsonRpcHandler {
}
match self.send_tx(tx.clone(), false).await? {
ProcessTxResponse::ValidTx | ProcessTxResponse::RequestRouted => {
self.tx_polling(near_jsonrpc_primitives::types::transactions::TransactionInfo::Transaction(tx)).await
self.tx_polling(tx.into()).await
telezhnaya marked this conversation as resolved.
Show resolved Hide resolved
}
network_client_response=> {
Err(
Expand Down
15 changes: 2 additions & 13 deletions core/primitives/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,7 @@ pub use crate::action::{

pub type LogEntry = String;

#[derive(
BorshSerialize,
BorshDeserialize,
PartialEq,
Eq,
Debug,
Clone,
serde::Serialize,
serde::Deserialize,
)]
#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, PartialEq, Eq, Debug, Clone)]
pub struct Transaction {
/// An account on which behalf transaction is signed
pub signer_id: AccountId,
Expand All @@ -53,9 +44,7 @@ impl Transaction {
}
}

#[derive(
BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, Eq, Debug, Clone,
)]
#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Eq, Debug, Clone)]
#[borsh(init=init)]
pub struct SignedTransaction {
pub transaction: Transaction,
Expand Down