|
1 | | -use crate::{Block, FeeHistory, Hex32, LogEntry, Nat256, RpcError, ValidationError}; |
2 | | -use alloy_primitives::{B256, U256}; |
| 1 | +use crate::{ |
| 2 | + Block, FeeHistory, Hex32, HexByte, LogEntry, Nat256, RpcError, RpcResult, TransactionReceipt, |
| 3 | + ValidationError, |
| 4 | +}; |
| 5 | +use alloy_primitives::{Address, B256, U256}; |
3 | 6 | use alloy_rpc_types::BlockTransactions; |
4 | 7 | use candid::Nat; |
5 | 8 | use num_bigint::BigUint; |
@@ -127,19 +130,106 @@ impl TryFrom<FeeHistory> for alloy_rpc_types::FeeHistory { |
127 | 130 | } |
128 | 131 | } |
129 | 132 |
|
| 133 | +impl TryFrom<TransactionReceipt> for alloy_rpc_types::TransactionReceipt { |
| 134 | + type Error = RpcError; |
| 135 | + |
| 136 | + fn try_from(receipt: TransactionReceipt) -> Result<Self, Self::Error> { |
| 137 | + Ok(Self { |
| 138 | + inner: alloy_consensus::ReceiptEnvelope::from_typed( |
| 139 | + alloy_consensus::TxType::try_from(receipt.tx_type)?, |
| 140 | + alloy_consensus::ReceiptWithBloom { |
| 141 | + receipt: alloy_consensus::Receipt { |
| 142 | + status: validate_receipt_status( |
| 143 | + &receipt.block_number, |
| 144 | + receipt.root, |
| 145 | + receipt.status, |
| 146 | + )?, |
| 147 | + cumulative_gas_used: try_from_nat256( |
| 148 | + receipt.cumulative_gas_used, |
| 149 | + "cumulative_gas_used", |
| 150 | + )?, |
| 151 | + logs: receipt |
| 152 | + .logs |
| 153 | + .into_iter() |
| 154 | + .map(alloy_rpc_types::Log::try_from) |
| 155 | + .collect::<RpcResult<Vec<alloy_rpc_types::Log>>>()?, |
| 156 | + }, |
| 157 | + logs_bloom: alloy_primitives::Bloom::from(receipt.logs_bloom), |
| 158 | + }, |
| 159 | + ), |
| 160 | + transaction_hash: B256::from(receipt.transaction_hash), |
| 161 | + transaction_index: Some(try_from_nat256( |
| 162 | + receipt.transaction_index, |
| 163 | + "transaction_index", |
| 164 | + )?), |
| 165 | + block_hash: Some(B256::from(receipt.block_hash)), |
| 166 | + block_number: Some(try_from_nat256(receipt.block_number, "block_number")?), |
| 167 | + gas_used: try_from_nat256(receipt.gas_used, "gas_used")?, |
| 168 | + effective_gas_price: try_from_nat256( |
| 169 | + receipt.effective_gas_price, |
| 170 | + "effective_gas_price", |
| 171 | + )?, |
| 172 | + blob_gas_used: None, |
| 173 | + blob_gas_price: None, |
| 174 | + from: Address::from(receipt.from), |
| 175 | + to: receipt.to.map(Address::from), |
| 176 | + contract_address: receipt.contract_address.map(Address::from), |
| 177 | + }) |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +impl TryFrom<HexByte> for alloy_consensus::TxType { |
| 182 | + type Error = RpcError; |
| 183 | + |
| 184 | + fn try_from(value: HexByte) -> Result<Self, Self::Error> { |
| 185 | + alloy_consensus::TxType::try_from(value.into_byte()).map_err(|e| { |
| 186 | + RpcError::ValidationError(ValidationError::Custom(format!( |
| 187 | + "Unable to parse transaction type: {e:?}" |
| 188 | + ))) |
| 189 | + }) |
| 190 | + } |
| 191 | +} |
| 192 | + |
130 | 193 | fn validate_difficulty(number: &Nat256, difficulty: Option<Nat256>) -> Result<U256, RpcError> { |
131 | 194 | const PARIS_BLOCK: u64 = 15_537_394; |
132 | 195 | if number.as_ref() < &Nat::from(PARIS_BLOCK) { |
133 | 196 | difficulty |
134 | 197 | .map(U256::from) |
135 | 198 | .ok_or(RpcError::ValidationError(ValidationError::Custom( |
136 | | - "Block before Paris upgrade but missing difficulty".into(), |
| 199 | + "Missing difficulty field in pre Paris upgrade block".into(), |
137 | 200 | ))) |
138 | 201 | } else { |
139 | 202 | match difficulty.map(U256::from) { |
140 | 203 | None | Some(U256::ZERO) => Ok(U256::ZERO), |
141 | 204 | _ => Err(RpcError::ValidationError(ValidationError::Custom( |
142 | | - "Block after Paris upgrade with non-zero difficulty".into(), |
| 205 | + "Post Paris upgrade block has non-zero difficulty".into(), |
| 206 | + ))), |
| 207 | + } |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +fn validate_receipt_status( |
| 212 | + number: &Nat256, |
| 213 | + root: Option<Hex32>, |
| 214 | + status: Option<Nat256>, |
| 215 | +) -> Result<alloy_consensus::Eip658Value, RpcError> { |
| 216 | + const BYZANTIUM_BLOCK: u64 = 4_370_000; |
| 217 | + if number.as_ref() < &Nat::from(BYZANTIUM_BLOCK) { |
| 218 | + match root { |
| 219 | + None => Err(RpcError::ValidationError(ValidationError::Custom( |
| 220 | + "Missing root field in transaction included before the Byzantium upgrade".into(), |
| 221 | + ))), |
| 222 | + Some(root) => Ok(alloy_consensus::Eip658Value::PostState(B256::from(root))), |
| 223 | + } |
| 224 | + } else { |
| 225 | + match status.map(U256::from) { |
| 226 | + None => Err(RpcError::ValidationError(ValidationError::Custom( |
| 227 | + "Missing status field in transaction included after the Byzantium upgrade".into(), |
| 228 | + ))), |
| 229 | + Some(U256::ZERO) => Ok(alloy_consensus::Eip658Value::Eip658(false)), |
| 230 | + Some(U256::ONE) => Ok(alloy_consensus::Eip658Value::Eip658(true)), |
| 231 | + Some(_) => Err(RpcError::ValidationError(ValidationError::Custom( |
| 232 | + "Post-Byzantium receipt has invalid status (expected 0 or 1)".into(), |
143 | 233 | ))), |
144 | 234 | } |
145 | 235 | } |
|
0 commit comments