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

Altered behaviour of GetTransactionStatus #2170

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
98 changes: 44 additions & 54 deletions docs/api/zilliqa/gettransactionstatus.doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,40 @@ transaction,get,status
# Description

Returns the status of a specified transaction. Note that in Zilliqa 2, this API will work for any transaction, and since there are no DS epochs any longer, the transaction pool is not cleared at a DS epoch boundary.
Status codes have been simplified since Zilliqa 1

## Response Fields

| Field | Description |
| ------------------- | ----------------------------------------------- |
| `_id` | Object ID (internal database field) |
| `epochInserted` | Tx epoch when this transaction was first logged |
| `epochUpdated` | Tx epoch when this transaction was last updated |
| `lastModified` | Timestamp for this transaction's last update |
| `modificationState` | See next table below |
| `status` | See next table below |
| Other fields | Transaction-related fields |
| Field | Description | Type |
|---------------------|----------------------------------------------------------------------------|---------|
| `ID` | Transaction hash as a string | string |
| `_id` | null | null |
| `amount` | Amount of ZIL transferred in the transaction | string |
| `data` | Data of the transaction | string |
| `epochInserted` | Block number of the transaction (or empty string if not yet in a block) | string |
| `epochUpdated` | Block number of the transaction (or empty string if not yet in a block) | string |
| `gasLimit` | Gas limit of the transaction | string |
| `gasPrice` | Gas price of the transaction | string |
| `lastModified` | Timestamp of transaction block (or now if not yet in a block) | string |
| `modificationState` | Modification state code (see table below) | integer |
| `status` | Status code (see table below) | integer |
| `nonce` | Nonce of the transaction | string |
| `senderAddr` | Sender address | string |
| `signature` | Signature | string |
| `success` | Transaction created successfully | boolean |
| `toAddr` | Recipient address | string |
| `version` | Version | string |



## Status Codes

| `modificationState` | `status` | Description |
| ------------------- | -------- | ----------------------------------------------------------------------- |
| 0 | 1 | Pending - Dispatched |
| 1 | 2 | Pending - Soft-confirmed (awaiting Tx block generation) |
| 1 | 4 | Pending - Nonce is higher than expected |
| 1 | 5 | Pending - Microblock gas limit exceeded |
| 1 | 6 | Pending - Consensus failure in network |
| 2 | 3 | Confirmed |
| 2 | 10 | Rejected - Transaction caused math error |
| 2 | 11 | Rejected - Scilla invocation error |
| 2 | 12 | Rejected - Contract account initialization error |
| 2 | 13 | Rejected - Invalid source account |
| 2 | 14 | Rejected - Gas limit higher than shard gas limit |
| 2 | 15 | Rejected - Unknown transaction type |
| 2 | 16 | Rejected - Transaction sent to wrong shard |
| 2 | 17 | Rejected - Contract & source account cross-shard issue |
| 2 | 18 | Rejected - Code size exceeded limit |
| 2 | 19 | Rejected - Transaction verification failed |
| 2 | 20 | Rejected - Gas limit too low |
| 2 | 21 | Rejected - Insufficient balance |
| 2 | 22 | Rejected - Insufficient gas to invoke Scilla checker |
| 2 | 23 | Rejected - Duplicate transaction exists |
| 2 | 24 | Rejected - Transaction with same nonce but same/higher gas price exists |
| 2 | 25 | Rejected - Invalid destination address |
| 2 | 26 | Rejected - Failed to add contract account to state |
| 2 | 27 | Rejected - Nonce is lower than expected |
| 2 | 255 | Rejected - Internal error |
| `modificationState` | `status` | Description |
| ------------------- | -------- | ---------------------------------------------- |
| 0 | 1 | Pending in mempool or in a non-finalized block |
| 2 | 3 | Confirmed (in a finalized block) |
| 1 | 4 | Queued in mempool |
| 2 | 255 | Error |


# Curl
Expand All @@ -71,25 +63,23 @@ curl -d '{
"id": "1",
"jsonrpc": "2.0",
"result": {
"ID": "1bb178b023f816e950d862f6505cd79a32bb97e71fd78441cbc3486940a2e1b7",
"_id": {
"$oid": "5fd053b0d127fe45cc5eea24"
},
"amount": "0",
"data": "{\"_tag\":\"AddAccount\",\"params\":[{\"vname\":\"address\",\"type\":\"ByStr20\",\"value\":\"0x0434cdcf27e2294b3539cb6ffe2cc328d7f9757e\"},{\"vname\":\"datetime_added\",\"type\":\"String\",\"value\":\"1607488428\"}]}",
"epochInserted": "2152402",
"epochUpdated": "2152405",
"gasLimit": "30000",
"gasPrice": "2000000000",
"lastModified": "1607488477842011",
"modificationState": 2,
"nonce": "131",
"senderAddr": "b8fe5ab2e66c71274216688cf852e6d9f10b94e7",
"signature": "0xBAA6964C66AE0608C6CEFBAAB69138E9358A1604C647DFFEF94E7022F2AB33D67F70802F71E934A0690BE4BA81CC3866B2FB668B29C528E6B77B1285533A2E2C",
"status": 3,
"ID": "0d151c2bd14f7fc6a14e7a363d34eb6c125e68c36cd12064ee874a93a30d3f09",
"_id": null,
"amount": "200000000000000",
"data": "",
"epochInserted": "4",
"epochUpdated": "4",
"gasLimit": "50000",
"gasPrice": "2000000016",
"lastModified": "3042000",
"modificationState": 1,
"nonce": "1",
"senderAddr": "0x0308484dfdba78ab585254e34c80c317cd97a26090c2a63d7f456c09344207994d",
"signature": "0x4201654cfa78e273aba03dcd563b1dfedd265a2e983c8efe8f9ea2a1d240601add4f37d1b7cdad605af73c193fec66c8a59ac4e8178048a6a3cf080a7632e6b7",
"status": 1,
"success": true,
"toAddr": "db4955ba4b1a957200ee0a36cf5f84eb4d7447e5",
"version": "21823489"
"toAddr": "0x00000000000000000000000000000000deadbeef",
"version": "45875201"
}
}
```
Expand Down
104 changes: 47 additions & 57 deletions zilliqa/src/api/types/zil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
to_hex::ToHex,
zilliqa::{TRANSACTIONS_PER_PAGE, TX_BLOCKS_PER_DS_BLOCK},
},
exec::{ScillaError, ScillaException},
exec::ScillaException,
message::Block,
schnorr,
scilla::ParamValue,
Expand Down Expand Up @@ -679,35 +679,9 @@ pub struct TransactionReceiptResponse {
#[derive(Serialize_repr, Deserialize_repr, Clone)]
#[repr(u8)] // Because otherwise it's weird that 255 is a special case
pub enum TxnStatusCode {
NotPresent = 0,
Dispatched = 1,
SoftConfirmed = 2,
Confirmed = 3,
// Pending
PresentNonceHigh = 4,
PresentGasExceeded = 5,
PresentValidConsensusNotReached = 6,
// RareDropped
MathError = 10,
FailScillaLib = 11,
FailContractInit = 12,
InvalidFromAccount = 13,
HighGasLimit = 14,
IncorrectTxnType = 15,
IncorrectShard = 16,
ContractCallWrongShard = 17,
HighByteSizeCode = 18,
VerifError = 19,
//
InsufficientGasLimit = 20,
InsufficientBalance = 21,
InsufficientGas = 22,
MempoolAlreadyPresent = 23,
MempoolSameNonceLowerGas = 24,
//
InvalidToAccount = 25,
FailContractAccountCreation = 26,
NonceTooLow = 27,
Error = 255, // MiscError
}

Expand Down Expand Up @@ -742,8 +716,21 @@ pub struct TransactionStatusResponse {
pub version: String,
}

#[derive(Clone, Copy)]
pub enum TransactionState {
Queued,
Pending,
Finalized,
Error,
}

impl TransactionStatusResponse {
pub fn new(tx: VerifiedTransaction, receipt: TransactionReceipt, block: Block) -> Result<Self> {
pub fn new(
tx: VerifiedTransaction,
success: bool,
block: Option<Block>,
state: TransactionState,
) -> Result<Self> {
let amount = tx.tx.zil_amount();
let gas_price = tx.tx.gas_price_per_scilla_gas();
let gas_limit = tx.tx.gas_limit_scilla();
Expand Down Expand Up @@ -800,47 +787,50 @@ impl TransactionStatusResponse {
tx.to_addr.is_some().then(|| hex::encode(&tx.payload)),
),
};
let status_code =
if receipt.accepted.is_some() && receipt.accepted.unwrap() && receipt.success {
TxnStatusCode::Confirmed
} else if receipt.success {
TxnStatusCode::Dispatched
} else {
let errors: Vec<ScillaError> =
receipt.errors.into_iter().flat_map(|(_k, v)| v).collect();
if errors.len() == 1 {
match errors[0] {
ScillaError::CallContractFailed => TxnStatusCode::FailScillaLib,
ScillaError::CreateContractFailed => TxnStatusCode::Error,
ScillaError::GasNotSufficient => TxnStatusCode::InsufficientGas,
ScillaError::BalanceTransferFailed => TxnStatusCode::InsufficientBalance,
_ => TxnStatusCode::Error,
}
} else {
TxnStatusCode::Error
}
};
let modification_state = if receipt.accepted.is_none() { 0 } else { 2 };
let (status_code, modification_state) = match state {
TransactionState::Error => (TxnStatusCode::Error, 2),
TransactionState::Finalized => (TxnStatusCode::Confirmed, 2),
TransactionState::Pending => (TxnStatusCode::Dispatched, 1),
TransactionState::Queued => (TxnStatusCode::PresentNonceHigh, 1),
};
let epoch_inserted = if let Some(block) = &block {
block.number().to_string()
} else {
"".to_string()
};
let epoch_updated = if let Some(block) = &block {
block.number().to_string()
} else {
"".to_string()
};
let last_modified = if let Some(block) = &block {
block
.timestamp()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_micros()
.to_string()
} else {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_micros()
.to_string()
};
Ok(Self {
id: tx.hash.to_string(),
_id: serde_json::Value::Null,
amount: amount.to_string(),
data: data.unwrap_or_default(),
epoch_inserted: block.number().to_string(),
epoch_updated: block.number().to_string(),
epoch_inserted,
epoch_updated,
gas_limit: gas_limit.to_string(),
gas_price: gas_price.to_string(),
last_modified: block
.timestamp()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_micros()
.to_string(),
last_modified,
modification_state,
status: status_code,
nonce: nonce.to_string(),
sender_addr: sender_pub_key,
signature,
success: receipt.success,
success,
to_addr: to_addr.to_hex(),
version: version.to_string(),
})
Expand Down
58 changes: 37 additions & 21 deletions zilliqa/src/api/zilliqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{

use alloy::{
consensus::SignableTransaction,
eips::BlockId,
eips::{BlockId, BlockNumberOrTag},
primitives::{Address, B256},
};
use anyhow::{anyhow, Result};
Expand All @@ -32,9 +32,9 @@ use super::{
self, BlockchainInfo, DSBlock, DSBlockHeaderVerbose, DSBlockListing, DSBlockListingResult,
DSBlockRateResult, DSBlockVerbose, GetCurrentDSCommResult, MinerInfo,
RecentTransactionsResponse, SWInfo, ShardingStructure, SmartContract, StateProofResponse,
TXBlockRateResult, TransactionBody, TransactionReceiptResponse, TransactionStatusResponse,
TxBlockListing, TxBlockListingResult, TxnBodiesForTxBlockExResponse,
TxnsForTxBlockExResponse,
TXBlockRateResult, TransactionBody, TransactionReceiptResponse, TransactionState,
TransactionStatusResponse, TxBlockListing, TxBlockListingResult,
TxnBodiesForTxBlockExResponse, TxnsForTxBlockExResponse,
},
};
use crate::{
Expand All @@ -44,7 +44,7 @@ use crate::{
exec::zil_contract_address,
message::Block,
node::Node,
pool::TxAddResult,
pool::{PendingOrQueued, TxAddResult},
schnorr,
scilla::{split_storage_key, storage_key, ParamValue},
state::Code,
Expand Down Expand Up @@ -1489,23 +1489,39 @@ fn get_transaction_status(
"Txn Hash not found".to_string(),
jsonrpc_error_data.clone(),
))?;
let receipt =
node.get_transaction_receipt(hash)?
.ok_or(jsonrpsee::types::ErrorObject::owned(
RPCErrorCode::RpcDatabaseError as i32,
"Txn receipt not found".to_string(),
jsonrpc_error_data.clone(),
))?;
let block = node
.get_block(receipt.block_hash)?
.ok_or(jsonrpsee::types::ErrorObject::owned(
RPCErrorCode::RpcDatabaseError as i32,
"Block not found".to_string(),
jsonrpc_error_data.clone(),
))?;
let receipt = node.get_transaction_receipt(hash)?;

let (block, success) = if let Some(receipt) = &receipt {
(node.get_block(receipt.block_hash)?, receipt.success)
} else {
(None, false)
};

// Determine transaction state
let state = if receipt.is_some_and(|receipt| !receipt.errors.is_empty()) {
TransactionState::Error
} else {
match &block {
Some(block) => {
let newest_finalized_block =
node.resolve_block_number(BlockNumberOrTag::Finalized)?;
if newest_finalized_block.is_some()
&& block.number() >= newest_finalized_block.unwrap().number()
{
TransactionState::Finalized
} else {
TransactionState::Pending
}
}
None => match node.consensus.get_pending_or_queued(&transaction)? {
Some(PendingOrQueued::Pending) => TransactionState::Pending,
Some(PendingOrQueued::Queued) => TransactionState::Queued,
None => panic!("Transaction not found in block or pending/queued"),
},
}
};

let res = TransactionStatusResponse::new(transaction, receipt, block)?;
Ok(res)
TransactionStatusResponse::new(transaction, success, block, state)
}

#[cfg(test)]
Expand Down
10 changes: 9 additions & 1 deletion zilliqa/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::{
MAX_COMMITTEE_SIZE,
},
node::{MessageSender, NetworkMessage},
pool::{TransactionPool, TxAddResult, TxPoolContent},
pool::{PendingOrQueued, TransactionPool, TxAddResult, TxPoolContent},
state::State,
sync::{Sync, SyncPeers},
time::SystemTime,
Expand Down Expand Up @@ -943,6 +943,14 @@ impl Consensus {
self.transaction_pool.preview_content(&self.state)
}

pub fn get_pending_or_queued(
&self,
txn: &VerifiedTransaction,
) -> Result<Option<PendingOrQueued>> {
self.transaction_pool
.get_pending_or_queued(&self.state, txn)
}

pub fn pending_transaction_count(&self, account: Address) -> u64 {
let current_nonce = self.state.must_get_account(account).nonce;

Expand Down
Loading