1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use serde_json::Value;
use unc_primitives::hash::CryptoHash;
use unc_primitives::types::AccountId;

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct RpcSendTransactionRequest {
    #[serde(rename = "signed_tx_base64")]
    pub signed_transaction: unc_primitives::transaction::SignedTransaction,
    #[serde(default)]
    pub wait_until: unc_primitives::views::TxExecutionStatus,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RpcTransactionStatusRequest {
    #[serde(flatten)]
    pub transaction_info: TransactionInfo,
    #[serde(default)]
    pub wait_until: unc_primitives::views::TxExecutionStatus,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum TransactionInfo {
    Transaction(SignedTransaction),
    TransactionId { tx_hash: CryptoHash, sender_account_id: AccountId },
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum SignedTransaction {
    #[serde(rename = "signed_tx_base64")]
    SignedTransaction(unc_primitives::transaction::SignedTransaction),
}

#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize, Clone)]
#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RpcTransactionError {
    #[error("An error happened during transaction execution: {context:?}")]
    InvalidTransaction {
        #[serde(skip_serializing)]
        context: unc_primitives::errors::InvalidTxError,
    },
    #[error("Node doesn't track this shard. Cannot determine whether the transaction is valid")]
    DoesNotTrackShard,
    #[error("Transaction with hash {transaction_hash} was routed")]
    RequestRouted { transaction_hash: unc_primitives::hash::CryptoHash },
    #[error("Transaction {requested_transaction_hash} doesn't exist")]
    UnknownTransaction { requested_transaction_hash: unc_primitives::hash::CryptoHash },
    #[error("The node reached its limits. Try again later. More details: {debug_info}")]
    InternalError { debug_info: String },
    #[error("Timeout")]
    TimeoutError,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct RpcTransactionResponse {
    #[serde(flatten)]
    pub final_execution_outcome: Option<unc_primitives::views::FinalExecutionOutcomeViewEnum>,
    pub final_execution_status: unc_primitives::views::TxExecutionStatus,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct RpcBroadcastTxSyncResponse {
    pub transaction_hash: unc_primitives::hash::CryptoHash,
}

impl TransactionInfo {
    pub fn from_signed_tx(tx: unc_primitives::transaction::SignedTransaction) -> Self {
        Self::Transaction(SignedTransaction::SignedTransaction(tx))
    }

    pub fn to_signed_tx(&self) -> Option<&unc_primitives::transaction::SignedTransaction> {
        match self {
            TransactionInfo::Transaction(tx) => match tx {
                SignedTransaction::SignedTransaction(tx) => Some(tx),
            },
            TransactionInfo::TransactionId { .. } => None,
        }
    }

    pub fn to_tx_hash_and_account(&self) -> (CryptoHash, &AccountId) {
        match self {
            TransactionInfo::Transaction(tx) => match tx {
                SignedTransaction::SignedTransaction(tx) => {
                    (tx.get_hash(), &tx.transaction.signer_id)
                }
            },
            TransactionInfo::TransactionId { tx_hash, sender_account_id } => {
                (*tx_hash, sender_account_id)
            }
        }
    }
}

impl From<unc_primitives::transaction::SignedTransaction> for TransactionInfo {
    fn from(transaction_info: unc_primitives::transaction::SignedTransaction) -> Self {
        Self::Transaction(SignedTransaction::SignedTransaction(transaction_info))
    }
}

impl From<unc_primitives::views::TxStatusView> for RpcTransactionResponse {
    fn from(view: unc_primitives::views::TxStatusView) -> Self {
        Self {
            final_execution_outcome: view.execution_outcome,
            final_execution_status: view.status,
        }
    }
}

impl From<RpcTransactionError> for crate::errors::RpcError {
    fn from(error: RpcTransactionError) -> Self {
        let error_data = match &error {
            RpcTransactionError::InvalidTransaction { context } => {
                if let Ok(value) =
                    serde_json::to_value(crate::errors::ServerError::TxExecutionError(
                        unc_primitives::errors::TxExecutionError::InvalidTxError(context.clone()),
                    ))
                {
                    value
                } else {
                    Value::String(error.to_string())
                }
            }
            _ => Value::String(error.to_string()),
        };

        let error_data_value = match serde_json::to_value(error) {
            Ok(value) => value,
            Err(err) => {
                return Self::new_internal_error(
                    None,
                    format!("Failed to serialize RpcTransactionError: {:?}", err),
                )
            }
        };

        Self::new_internal_or_handler_error(Some(error_data), error_data_value)
    }
}