Skip to content
This repository has been archived by the owner on Dec 26, 2024. It is now read-only.

feat(JSON-RPC): get_transaction_receipt supports pending #1284

Merged
merged 1 commit into from
Oct 22, 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
165 changes: 130 additions & 35 deletions crates/papyrus_rpc/resources/V0_4_0/starknet_api_openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2199,6 +2199,31 @@
}
]
},
"PENDING_INVOKE_TXN_RECEIPT": {
"title": "Invoke Transaction Receipt",
"allOf": [
{
"title": "Type",
"type": "object",
"properties": {
"type": {
"title": "Type",
"type": "string",
"enum": [
"INVOKE"
]
}
},
"required": [
"type"
]
},
{
"title": "Common receipt properties",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
}
]
},
"DECLARE_TXN_RECEIPT": {
"title": "Declare Transaction Receipt",
"allOf": [
Expand All @@ -2224,6 +2249,31 @@
}
]
},
"PENDING_DECLARE_TXN_RECEIPT": {
"title": "Declare Transaction Receipt",
"allOf": [
{
"title": "Declare txn receipt",
"type": "object",
"properties": {
"type": {
"title": "Declare",
"type": "string",
"enum": [
"DECLARE"
]
}
},
"required": [
"type"
]
},
{
"title": "Common receipt properties",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
}
]
},
"DEPLOY_ACCOUNT_TXN_RECEIPT": {
"title": "Deploy Account Transaction Receipt",
"allOf": [
Expand Down Expand Up @@ -2255,6 +2305,37 @@
}
]
},
"PENDING_DEPLOY_ACCOUNT_TXN_RECEIPT": {
"title": "Deploy Account Transaction Receipt",
"allOf": [
{
"title": "Common receipt properties",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
},
{
"title": "DeployAccount txn receipt",
"type": "object",
"properties": {
"type": {
"title": "Deploy account",
"type": "string",
"enum": [
"DEPLOY_ACCOUNT"
]
},
"contract_address": {
"title": "Contract address",
"description": "The address of the deployed contract",
"$ref": "#/components/schemas/FELT"
}
},
"required": [
"type",
"contract_address"
]
}
]
},
"DEPLOY_TXN_RECEIPT": {
"title": "Deploy Transaction Receipt",
"allOf": [
Expand Down Expand Up @@ -2312,6 +2393,32 @@
}
]
},
"PENDING_L1_HANDLER_TXN_RECEIPT": {
"title": "L1 Handler Transaction Receipt",
"description": "receipt for l1 handler transaction",
"allOf": [
{
"title": "Transaction type",
"type": "object",
"properties": {
"type": {
"title": "type",
"type": "string",
"enum": [
"L1_HANDLER"
]
}
},
"required": [
"type"
]
},
{
"title": "Common receipt properties",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
}
]
},
"TXN_RECEIPT": {
"title": "Transaction Receipt",
"oneOf": [
Expand Down Expand Up @@ -2341,6 +2448,27 @@
}
]
},
"PENDING_TXN_RECEIPT": {
"title": "Transaction Receipt",
"oneOf": [
{
"title": "Pending Invoke transaction receipt",
"$ref": "#/components/schemas/PENDING_INVOKE_TXN_RECEIPT"
},
{
"title": "Pending L1 handler transaction receipt",
"$ref": "#/components/schemas/PENDING_L1_HANDLER_TXN_RECEIPT"
},
{
"title": "Pending Declare transaction receipt",
"$ref": "#/components/schemas/PENDING_DECLARE_TXN_RECEIPT"
},
{
"title": "Pending Deploy account transaction receipt",
"$ref": "#/components/schemas/PENDING_DEPLOY_ACCOUNT_TXN_RECEIPT"
}
]
},
"PENDING_COMMON_RECEIPT_PROPERTIES": {
"title": "Pending common receipt properties",
"description": "Common properties for a pending transaction receipt",
Expand Down Expand Up @@ -2402,41 +2530,8 @@
"events",
"finality_status",
"execution_status"
]
},
"PENDING_DEPLOY_TXN_RECEIPT": {
"title": "Pending deploy Transaction Receipt",
"allOf": [
{
"title": "Common receipt properties",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
},
{
"type": "object",
"title": "Contract address",
"properties": {
"contract_address": {
"title": "Contract address",
"description": "The address of the deployed contract",
"$ref": "#/components/schemas/FELT"
}
}
}
]
},
"PENDING_TXN_RECEIPT": {
"title": "Pending Transaction Receipt",
"oneOf": [
{
"title": "Pending deploy transaction receipt",
"$ref": "#/components/schemas/PENDING_DEPLOY_TXN_RECEIPT"
},
{
"title": "Pending common receipt properties",
"$comment": "Used for pending invoke and declare transaction receipts",
"$ref": "#/components/schemas/PENDING_COMMON_RECEIPT_PROPERTIES"
}
]
],
"additionalProperties": false
},
"MSG_TO_L1": {
"title": "Message to L1",
Expand Down
118 changes: 75 additions & 43 deletions crates/papyrus_rpc/src/v0_4_0/api/api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ use super::super::broadcasted_transaction::{
use super::super::state::{AcceptedStateUpdate, PendingStateUpdate, StateUpdate};
use super::super::transaction::{
Event,
GeneralTransactionReceipt,
PendingTransactionFinalityStatus,
PendingTransactionOutput,
PendingTransactionReceipt,
Transaction,
TransactionOutput,
TransactionReceipt,
Expand Down Expand Up @@ -234,9 +238,16 @@ impl JsonRpcV0_4Server for JsonRpcServerV0_4Impl {
) -> RpcResult<TransactionWithHash> {
let txn = self.storage_reader.begin_ro_txn().map_err(internal_server_error)?;

let Some(transaction_index) =
if let Some(transaction_index) =
txn.get_transaction_idx_by_hash(&transaction_hash).map_err(internal_server_error)?
else {
{
let transaction = txn
.get_transaction(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;

Ok(TransactionWithHash { transaction: transaction.try_into()?, transaction_hash })
} else {
// The transaction is not in any non-pending block. Search for it in the pending block
// and if it's not found, return error.
let client_transaction = self
Expand All @@ -256,14 +267,7 @@ impl JsonRpcV0_4Server for JsonRpcServerV0_4Impl {
transaction: starknet_api_transaction.try_into()?,
transaction_hash,
});
};

let transaction = txn
.get_transaction(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;

Ok(TransactionWithHash { transaction: transaction.try_into()?, transaction_hash })
}
}

#[instrument(skip(self), level = "debug", err, ret)]
Expand Down Expand Up @@ -361,50 +365,78 @@ impl JsonRpcV0_4Server for JsonRpcServerV0_4Impl {
}

#[instrument(skip(self), level = "debug", err, ret)]
fn get_transaction_receipt(
async fn get_transaction_receipt(
&self,
transaction_hash: TransactionHash,
) -> RpcResult<TransactionReceipt> {
) -> RpcResult<GeneralTransactionReceipt> {
let txn = self.storage_reader.begin_ro_txn().map_err(internal_server_error)?;

let transaction_index = txn
.get_transaction_idx_by_hash(&transaction_hash)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;
if let Some(transaction_index) =
txn.get_transaction_idx_by_hash(&transaction_hash).map_err(internal_server_error)?
{
let block_number = transaction_index.0;
let status = get_block_status(&txn, block_number)?;

// rejected blocks should not be a part of the API so we early return here.
// this assumption also holds for the conversion from block status to transaction
// finality status where we set rejected blocks to unreachable.
if status == BlockStatus::Rejected {
return Err(ErrorObjectOwned::from(BLOCK_NOT_FOUND))?;
}

let block_number = transaction_index.0;
let status = get_block_status(&txn, block_number)?;
let block_hash = get_block_header_by_number::<_, BlockHeader>(&txn, block_number)
.map_err(internal_server_error)?
.block_hash;

// rejected blocks should not be a part of the API so we early return here.
// this assumption also holds for the conversion from block status to transaction finality
// status where we set rejected blocks to unreachable.
if status == BlockStatus::Rejected {
return Err(ErrorObjectOwned::from(BLOCK_NOT_FOUND))?;
}
let thin_tx_output = txn
.get_transaction_output(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;

let block_hash = get_block_header_by_number::<_, BlockHeader>(&txn, block_number)
.map_err(internal_server_error)?
.block_hash;
let events = txn
.get_transaction_events(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;

let thin_tx_output = txn
.get_transaction_output(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;
let output = TransactionOutput::from_thin_transaction_output(thin_tx_output, events);

let events = txn
.get_transaction_events(transaction_index)
.map_err(internal_server_error)?
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;
Ok(GeneralTransactionReceipt::TransactionReceipt(TransactionReceipt {
finality_status: status.into(),
transaction_hash,
block_hash,
block_number,
output,
}))
} else {
// The transaction is not in any non-pending block. Search for it in the pending block
// and if it's not found, return error.

let output = TransactionOutput::from_thin_transaction_output(thin_tx_output, events);
// TODO(shahak): Consider cloning the transactions and the receipts in order to free
// the lock sooner (Check which is better).
let pending_block = &self.pending_data.read().await.block;

Ok(TransactionReceipt {
finality_status: status.into(),
transaction_hash,
block_hash,
block_number,
output,
})
let client_transaction_receipt = pending_block
.transaction_receipts
.iter()
.find(|receipt| receipt.transaction_hash == transaction_hash)
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?
.clone();
let client_transaction = &pending_block
.transactions
.iter()
.find(|transaction| transaction.transaction_hash() == transaction_hash)
.ok_or_else(|| ErrorObjectOwned::from(TRANSACTION_HASH_NOT_FOUND))?;
let starknet_api_output =
client_transaction_receipt.into_starknet_api_transaction_output(client_transaction);
let output =
PendingTransactionOutput::try_from(TransactionOutput::from(starknet_api_output))?;
Ok(GeneralTransactionReceipt::PendingTransactionReceipt(PendingTransactionReceipt {
// ACCEPTED_ON_L2 is the only finality status of a pending transaction.
finality_status: PendingTransactionFinalityStatus::AcceptedOnL2,
transaction_hash,
output,
}))
}
}

#[instrument(skip(self), level = "debug", err, ret)]
Expand Down
6 changes: 3 additions & 3 deletions crates/papyrus_rpc/src/v0_4_0/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ use super::transaction::{
DeployAccountTransaction,
DeployAccountTransactionV1,
Event,
GeneralTransactionReceipt,
InvokeTransaction,
InvokeTransactionV0,
InvokeTransactionV1,
TransactionReceipt,
TransactionWithHash,
};
use super::write_api_result::{AddDeclareOkResult, AddDeployAccountOkResult, AddInvokeOkResult};
Expand Down Expand Up @@ -111,10 +111,10 @@ pub trait JsonRpc {

/// Gets the transaction receipt by the transaction hash.
#[method(name = "getTransactionReceipt")]
fn get_transaction_receipt(
async fn get_transaction_receipt(
&self,
transaction_hash: TransactionHash,
) -> RpcResult<TransactionReceipt>;
) -> RpcResult<GeneralTransactionReceipt>;

/// Gets the contract class definition associated with the given hash.
#[method(name = "getClass")]
Expand Down
Loading
Loading