Skip to content

Commit

Permalink
rpc: allow parse object at tx status request
Browse files Browse the repository at this point in the history
  • Loading branch information
telezhnaya committed Oct 5, 2023
1 parent d530a8b commit 71089b5
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 18 deletions.
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
12 changes: 10 additions & 2 deletions chain/jsonrpc-primitives/src/types/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ pub struct RpcBroadcastTransactionRequest {
pub signed_transaction: near_primitives::transaction::SignedTransaction,
}

#[derive(Debug)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RpcTransactionStatusCommonRequest {
#[serde(flatten)]
pub transaction_info: TransactionInfo,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum TransactionInfo {
Transaction(near_primitives::transaction::SignedTransaction),
TransactionId {
Expand Down Expand Up @@ -51,6 +53,12 @@ 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<RpcTransactionError> for crate::errors::RpcError {
fn from(error: RpcTransactionError) -> Self {
let error_data = match &error {
Expand Down
1 change: 1 addition & 0 deletions chain/jsonrpc/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ mod params {

/// If value hasn’t been parsed yet, tries to deserialise it directly
/// into `T` using given parse function.
#[allow(unused)]
pub fn unwrap_or_else(
self,
func: impl FnOnce(Value) -> Result<T, RpcParseError>,
Expand Down
81 changes: 66 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 @@ -9,25 +7,33 @@ use near_jsonrpc_primitives::types::transactions::{
TransactionInfo,
};
use near_primitives::borsh::BorshDeserialize;
use near_primitives::serialize::from_base64;
use near_primitives::transaction::SignedTransaction;

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_or_parse()?;
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 })
let transaction_info = Params::<RpcTransactionStatusCommonRequest>::new(value)
.try_singleton(|signed_tx| {
decode_signed_transaction(signed_tx)
.map(TransactionInfo::Transaction)
.map(|x| x.into())
})
.try_pair(|hash, account_id| {
Ok(TransactionInfo::TransactionId { hash, account_id }.into())
})
.unwrap_or_parse()?;
Ok(transaction_info)
}
}

Expand All @@ -52,12 +58,57 @@ 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 = from_base64(&value)
.map_err(|e| RpcParseError(format!("Failed to decode transaction: {}", e)))?;
SignedTransaction::try_from_slice(&bytes)
.map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))
}

#[cfg(test)]
mod tests {
use crate::api::RpcRequest;
use near_jsonrpc_primitives::types::transactions::{
RpcBroadcastTransactionRequest, RpcTransactionStatusCommonRequest,
};
use near_primitives::borsh::BorshSerialize;
use near_primitives::hash::CryptoHash;
use near_primitives::serialize::to_base64;
use near_primitives::transaction::SignedTransaction;
use std::str::FromStr;

#[test]
fn test_serialize_tx_status_params_as_vec() {
let params =
serde_json::json!(["6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", "sender.testnet"]);
assert!(RpcTransactionStatusCommonRequest::parse(params).is_ok());
}

#[test]
fn test_serialize_tx_status_params_as_object() {
let params = serde_json::json!({"hash": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", "account_id": "sender.testnet"});
assert!(RpcTransactionStatusCommonRequest::parse(params).is_ok());
}

#[test]
fn test_serialize_tx_status_params_as_binary_signed_tx() {
let tx = SignedTransaction::empty(
CryptoHash::from_str("7nsuuitwS7xcdGnD9JgrE22cRB2vf2VS4yh1N9S71F4d").unwrap(),
);
let bytes_tx = tx.try_to_vec().unwrap();
let str_tx = to_base64(&bytes_tx);
let params = serde_json::json!([str_tx]);
assert!(RpcTransactionStatusCommonRequest::parse(params).is_ok());
}

#[test]
fn test_serialize_send_tx_params_as_binary_signed_tx() {
let tx = SignedTransaction::empty(
CryptoHash::from_str("7nsuuitwS7xcdGnD9JgrE22cRB2vf2VS4yh1N9S71F4d").unwrap(),
);
let bytes_tx = tx.try_to_vec().unwrap();
let str_tx = to_base64(&bytes_tx);
let params = serde_json::json!([str_tx]);
assert!(RpcBroadcastTransactionRequest::parse(params).is_ok());
}
}

0 comments on commit 71089b5

Please sign in to comment.