rpc-v2/transaction: Generate Invalid events and add tests (#3784)

This PR ensures that the transaction API generates an `Invalid` events
for transaction bytes that fail to decode.

The spec mentioned the `Invalid` event at the jsonrpc error section,
however this spec PR makes things clearer:
- https://github.com/paritytech/json-rpc-interface-spec/pull/146

While at it have discovered an inconsistency with the generated events.
The drop event from the transaction pool was incorrectly mapped to the
`invalid` event.

Added tests for the API stabilize the API soon:
- https://github.com/paritytech/json-rpc-interface-spec/pull/144


Closes: https://github.com/paritytech/polkadot-sdk/issues/3083


cc @paritytech/subxt-team

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
Alexandru Vasile
2024-04-09 16:57:44 +03:00
committed by GitHub
parent a26d25d5c7
commit 598e95577d
5 changed files with 224 additions and 22 deletions
@@ -28,8 +28,8 @@ use crate::{
};
use codec::Decode;
use futures::{StreamExt, TryFutureExt};
use jsonrpsee::{core::async_trait, types::error::ErrorObject, PendingSubscriptionSink};
use sc_rpc::utils::pipe_from_stream;
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
use sc_rpc::utils::{pipe_from_stream, to_sub_message};
use sc_transaction_pool_api::{
error::IntoPoolError, BlockHash, TransactionFor, TransactionPool, TransactionSource,
TransactionStatus,
@@ -39,6 +39,8 @@ use sp_core::Bytes;
use sp_runtime::traits::Block as BlockT;
use std::sync::Arc;
pub(crate) const LOG_TARGET: &str = "rpc-spec-v2";
/// An API for transaction RPC calls.
pub struct Transaction<Pool, Client> {
/// Substrate client.
@@ -63,13 +65,6 @@ impl<Pool, Client> Transaction<Pool, Client> {
/// some unique transactions via RPC and have them included in the pool.
const TX_SOURCE: TransactionSource = TransactionSource::External;
/// Extrinsic has an invalid format.
///
/// # Note
///
/// This is similar to the old `author` API error code.
const BAD_FORMAT: i32 = 1001;
#[async_trait]
impl<Pool, Client> TransactionApiServer<BlockHash<Pool>> for Transaction<Pool, Client>
where
@@ -83,17 +78,21 @@ where
let pool = self.pool.clone();
let fut = async move {
// This is the only place where the RPC server can return an error for this
// subscription. Other defects must be signaled as events to the sink.
let decoded_extrinsic = match TransactionFor::<Pool>::decode(&mut &xt[..]) {
Ok(decoded_extrinsic) => decoded_extrinsic,
Err(e) => {
let err = ErrorObject::owned(
BAD_FORMAT,
format!("Extrinsic has invalid format: {}", e),
None::<()>,
log::debug!(target: LOG_TARGET, "Extrinsic bytes cannot be decoded: {:?}", e);
let Ok(sink) = pending.accept().await else { return };
// The transaction is invalid.
let msg = to_sub_message(
&sink,
&TransactionEvent::Invalid::<BlockHash<Pool>>(TransactionError {
error: "Extrinsic bytes cannot be decoded".into(),
}),
);
let _ = pending.reject(err).await;
let _ = sink.send(msg).await;
return
},
};
@@ -147,7 +146,7 @@ pub fn handle_event<Hash: Clone, BlockHash: Clone>(
TransactionStatus::Usurped(_) => Some(TransactionEvent::Invalid(TransactionError {
error: "Extrinsic was rendered invalid by another extrinsic".into(),
})),
TransactionStatus::Dropped => Some(TransactionEvent::Invalid(TransactionError {
TransactionStatus::Dropped => Some(TransactionEvent::Dropped(TransactionDropped {
error: "Extrinsic dropped from the pool due to exceeding limits".into(),
})),
TransactionStatus::Invalid => Some(TransactionEvent::Invalid(TransactionError {