Skip to content

Commit

Permalink
Allow querying transaction from the status (#2061)
Browse files Browse the repository at this point in the history
Closes #1724

### Before requesting review
- [x] I have reviewed the code myself
  • Loading branch information
xgreenx authored Aug 9, 2024
1 parent f37fbfb commit 005a0e6
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- [2061](https://github.com/FuelLabs/fuel-core/pull/2061): Allow querying filled transaction body from the status.

### Changed
-[2064](https://github.com/FuelLabs/fuel-core/pull/2064): Allow gas price metadata values to be overridden with config

Expand Down
Binary file not shown.
2 changes: 2 additions & 0 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ type FailureStatus {
transactionId: TransactionId!
blockHeight: U32!
block: Block!
transaction: Transaction!
time: Tai64Timestamp!
reason: String!
programState: ProgramState
Expand Down Expand Up @@ -1122,6 +1123,7 @@ type SuccessStatus {
transactionId: TransactionId!
blockHeight: U32!
block: Block!
transaction: Transaction!
time: Tai64Timestamp!
programState: ProgramState
receipts: [Receipt!]!
Expand Down
17 changes: 12 additions & 5 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,16 +587,17 @@ impl FuelClient {

/// Get the status of a transaction
pub async fn transaction_status(&self, id: &TxId) -> io::Result<TransactionStatus> {
let query = schema::tx::TransactionQuery::build(TxIdArgs { id: (*id).into() });
let query =
schema::tx::TransactionStatusQuery::build(TxIdArgs { id: (*id).into() });

let tx = self.query(query).await?.transaction.ok_or_else(|| {
let status = self.query(query).await?.transaction.ok_or_else(|| {
io::Error::new(
ErrorKind::NotFound,
format!("status not found for transaction {id} "),
)
})?;

let status = tx
let status = status
.status
.ok_or_else(|| {
io::Error::new(
Expand Down Expand Up @@ -684,7 +685,8 @@ impl FuelClient {
}

pub async fn receipts(&self, id: &TxId) -> io::Result<Option<Vec<Receipt>>> {
let query = schema::tx::TransactionQuery::build(TxIdArgs { id: (*id).into() });
let query =
schema::tx::TransactionStatusQuery::build(TxIdArgs { id: (*id).into() });

let tx = self.query(query).await?.transaction.ok_or_else(|| {
io::Error::new(ErrorKind::NotFound, format!("transaction {id} not found"))
Expand Down Expand Up @@ -1018,6 +1020,11 @@ impl FuelClient {

let transaction = self.query(query).await?.transaction;

Ok(transaction.map(|tx| tx.try_into()).transpose()?)
Ok(transaction
.map(|tx| {
let response: TransactionResponse = tx.try_into()?;
Ok::<_, ConversionError>(response.transaction)
})
.transpose()?)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ query($id: TransactionId!) {
time
}
... on SuccessStatus {
transactionId
blockHeight
time
programState {
Expand Down Expand Up @@ -55,7 +54,6 @@ query($id: TransactionId!) {
reason
}
... on FailureStatus {
transactionId
blockHeight
time
reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ query($owner: Address!, $after: String, $before: String, $first: Int, $last: Int
time
}
... on SuccessStatus {
transactionId
blockHeight
time
programState {
Expand Down Expand Up @@ -58,7 +57,6 @@ query($owner: Address!, $after: String, $before: String, $first: Int, $last: Int
reason
}
... on FailureStatus {
transactionId
blockHeight
time
reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ query($after: String, $before: String, $first: Int, $last: Int) {
time
}
... on SuccessStatus {
transactionId
blockHeight
time
programState {
Expand Down Expand Up @@ -58,7 +57,6 @@ query($after: String, $before: String, $first: Int, $last: Int) {
reason
}
... on FailureStatus {
transactionId
blockHeight
time
reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ query($id: TransactionId!) {
time
}
... on SuccessStatus {
transactionId
blockHeight
time
programState {
Expand Down Expand Up @@ -140,7 +139,6 @@ query($id: TransactionId!) {
reason
}
... on FailureStatus {
transactionId
blockHeight
time
reason
Expand Down
38 changes: 34 additions & 4 deletions crates/client/src/client/schema/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,19 @@ pub struct TxIdArgs {
)]
pub struct TransactionQuery {
#[arguments(id: $id)]
pub transaction: Option<OpaqueTransaction>,
pub transaction: Option<OpaqueTransactionWithStatus>,
}

/// Retrieves the transaction in opaque form
#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Query",
variables = "TxIdArgs"
)]
pub struct TransactionStatusQuery {
#[arguments(id: $id)]
pub transaction: Option<OpaqueTransactionStatus>,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
Expand Down Expand Up @@ -92,13 +104,25 @@ impl TryFrom<TransactionConnection> for PaginatedResult<TransactionResponse, Str
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct TransactionEdge {
pub cursor: String,
pub node: OpaqueTransaction,
pub node: OpaqueTransactionWithStatus,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(graphql_type = "Transaction", schema_path = "./assets/schema.sdl")]
pub struct OpaqueTransaction {
pub raw_payload: HexString,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(graphql_type = "Transaction", schema_path = "./assets/schema.sdl")]
pub struct OpaqueTransactionWithStatus {
pub raw_payload: HexString,
pub status: Option<TransactionStatus>,
}

#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(graphql_type = "Transaction", schema_path = "./assets/schema.sdl")]
pub struct OpaqueTransactionStatus {
pub status: Option<TransactionStatus>,
}

Expand Down Expand Up @@ -178,7 +202,8 @@ pub struct SubmittedStatus {
#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct SuccessStatus {
pub transaction_id: TransactionId,
#[cfg(feature = "test-helpers")]
pub transaction: OpaqueTransaction,
pub block_height: U32,
pub time: Tai64Timestamp,
pub program_state: Option<ProgramState>,
Expand All @@ -190,7 +215,8 @@ pub struct SuccessStatus {
#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct FailureStatus {
pub transaction_id: TransactionId,
#[cfg(feature = "test-helpers")]
pub transaction: OpaqueTransaction,
pub block_height: U32,
pub time: Tai64Timestamp,
pub reason: String,
Expand Down Expand Up @@ -418,6 +444,7 @@ pub mod tests {
use crate::client::schema::Bytes;
use fuel_core_types::fuel_types::canonical::Serialize;

#[cfg(not(feature = "test-helpers"))]
#[test]
fn transparent_transaction_by_id_query_gql_output() {
use cynic::QueryBuilder;
Expand All @@ -427,6 +454,7 @@ pub mod tests {
insta::assert_snapshot!(operation.query)
}

#[cfg(not(feature = "test-helpers"))]
#[test]
fn opaque_transaction_by_id_query_gql_output() {
use cynic::QueryBuilder;
Expand All @@ -436,6 +464,7 @@ pub mod tests {
insta::assert_snapshot!(operation.query)
}

#[cfg(not(feature = "test-helpers"))]
#[test]
fn transactions_connection_query_gql_output() {
use cynic::QueryBuilder;
Expand All @@ -448,6 +477,7 @@ pub mod tests {
insta::assert_snapshot!(operation.query)
}

#[cfg(not(feature = "test-helpers"))]
#[test]
fn transactions_by_owner_gql_output() {
use cynic::QueryBuilder;
Expand Down
14 changes: 11 additions & 3 deletions crates/client/src/client/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub use node_info::NodeInfo;
use crate::client::schema::{
relayed_tx::RelayedTransactionStatus as SchemaRelayedTransactionStatus,
tx::{
OpaqueTransaction,
OpaqueTransactionWithStatus,
TransactionStatus as SchemaTxStatus,
},
ConversionError,
Expand Down Expand Up @@ -96,6 +96,8 @@ pub enum TransactionStatus {
submitted_at: Tai64,
},
Success {
#[cfg(feature = "test-helpers")]
transaction: Transaction,
block_height: BlockHeight,
time: Tai64,
program_state: Option<ProgramState>,
Expand All @@ -107,6 +109,8 @@ pub enum TransactionStatus {
reason: String,
},
Failure {
#[cfg(feature = "test-helpers")]
transaction: Transaction,
block_height: BlockHeight,
time: Tai64,
reason: String,
Expand All @@ -126,6 +130,8 @@ impl TryFrom<SchemaTxStatus> for TransactionStatus {
submitted_at: s.time.0,
},
SchemaTxStatus::SuccessStatus(s) => TransactionStatus::Success {
#[cfg(feature = "test-helpers")]
transaction: s.transaction.try_into()?,
block_height: s.block_height.into(),
time: s.time.0,
program_state: s.program_state.map(TryInto::try_into).transpose()?,
Expand All @@ -138,6 +144,8 @@ impl TryFrom<SchemaTxStatus> for TransactionStatus {
total_fee: s.total_fee.0,
},
SchemaTxStatus::FailureStatus(s) => TransactionStatus::Failure {
#[cfg(feature = "test-helpers")]
transaction: s.transaction.try_into()?,
block_height: s.block_height.into(),
time: s.time.0,
reason: s.reason,
Expand All @@ -160,10 +168,10 @@ impl TryFrom<SchemaTxStatus> for TransactionStatus {
}
}

impl TryFrom<OpaqueTransaction> for TransactionResponse {
impl TryFrom<OpaqueTransactionWithStatus> for TransactionResponse {
type Error = ConversionError;

fn try_from(value: OpaqueTransaction) -> Result<Self, Self::Error> {
fn try_from(value: OpaqueTransactionWithStatus) -> Result<Self, Self::Error> {
let bytes = value.raw_payload.0 .0;
let tx: Transaction = Transaction::from_bytes(bytes.as_slice())
.map_err(ConversionError::TransactionFromBytesError)?;
Expand Down
15 changes: 15 additions & 0 deletions crates/fuel-core/src/schema/tx/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
},
query::{
SimpleBlockData,
SimpleTransactionData,
TransactionQueryData,
},
schema::{
Expand Down Expand Up @@ -188,6 +189,13 @@ impl SuccessStatus {
Ok(block.into())
}

#[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")]
async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result<Transaction> {
let query = ctx.read_view()?;
let transaction = query.transaction(&self.tx_id)?;
Ok(Transaction::from_tx(self.tx_id, transaction))
}

async fn time(&self) -> Tai64Timestamp {
Tai64Timestamp(self.time)
}
Expand Down Expand Up @@ -237,6 +245,13 @@ impl FailureStatus {
Ok(block.into())
}

#[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")]
async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result<Transaction> {
let query = ctx.read_view()?;
let transaction = query.transaction(&self.tx_id)?;
Ok(Transaction::from_tx(self.tx_id, transaction))
}

async fn time(&self) -> Tai64Timestamp {
Tai64Timestamp(self.time)
}
Expand Down
27 changes: 26 additions & 1 deletion tests/tests/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ use fuel_core_poa::service::Mode;
use fuel_core_types::{
fuel_asm::*,
fuel_crypto::SecretKey,
fuel_tx::*,
fuel_tx::{
field::ReceiptsRoot,
*,
},
fuel_types::ChainId,
};
use itertools::Itertools;
Expand Down Expand Up @@ -268,6 +271,28 @@ async fn get_transparent_transaction_by_id() {
assert_eq!(opaque_tx, transparent_transaction);
}

#[tokio::test]
async fn get_executed_transaction_from_status() {
let srv = FuelService::new_node(Config::local_node()).await.unwrap();
let client = FuelClient::from(srv.bound_address);

// Given
let transaction = Transaction::default_test_tx();
let receipt_root_before_execution = *transaction.as_script().unwrap().receipts_root();
assert_eq!(receipt_root_before_execution, Bytes32::zeroed());

// When
let result = client.submit_and_await_commit(&transaction).await;

// Then
let status = result.expect("Expected executed transaction");
let TransactionStatus::Success { transaction, .. } = status else {
panic!("Not successful transaction")
};
let receipt_root_after_execution = *transaction.as_script().unwrap().receipts_root();
assert_ne!(receipt_root_after_execution, Bytes32::zeroed());
}

#[tokio::test]
async fn get_transactions() {
let alice = Address::from([1; 32]);
Expand Down

0 comments on commit 005a0e6

Please sign in to comment.