feat(rpc/ots): implement ots_getContractCreator (#9236)

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Alexey Shekhirin <a.shekhirin@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Delweng
2024-07-09 04:39:50 +08:00
committed by GitHub
parent 17af21f826
commit 94f1adfa7c
2 changed files with 76 additions and 9 deletions

View File

@@ -342,9 +342,7 @@ where
.err()
.unwrap()
));
assert!(is_unimplemented(
OtterscanClient::get_contract_creator(client, address).await.err().unwrap()
));
assert!(OtterscanClient::get_contract_creator(client, address).await.unwrap().is_none());
}
#[tokio::test(flavor = "multi_thread")]

View File

@@ -4,15 +4,22 @@ use jsonrpsee::core::RpcResult;
use reth_primitives::{Address, BlockId, BlockNumberOrTag, TxHash, B256};
use reth_rpc_api::{EthApiServer, OtterscanServer};
use reth_rpc_eth_api::helpers::TraceExt;
use reth_rpc_eth_types::EthApiError;
use reth_rpc_server_types::result::internal_rpc_err;
use reth_rpc_types::{
trace::otterscan::{
BlockDetails, ContractCreator, InternalOperation, OperationType, OtsBlockTransactions,
OtsReceipt, OtsTransactionReceipt, TraceEntry, TransactionsWithReceipts,
trace::{
otterscan::{
BlockDetails, ContractCreator, InternalOperation, OperationType, OtsBlockTransactions,
OtsReceipt, OtsTransactionReceipt, TraceEntry, TransactionsWithReceipts,
},
parity::{Action, CreateAction, CreateOutput, TraceOutput},
},
BlockTransactions, Header, Transaction,
};
use revm_inspectors::transfer::{TransferInspector, TransferKind};
use revm_inspectors::{
tracing::TracingInspectorConfig,
transfer::{TransferInspector, TransferKind},
};
use revm_primitives::ExecutionResult;
const API_LEVEL: u64 = 8;
@@ -208,7 +215,69 @@ where
}
/// Handler for `getContractCreator`
async fn get_contract_creator(&self, _address: Address) -> RpcResult<Option<ContractCreator>> {
Err(internal_rpc_err("unimplemented"))
async fn get_contract_creator(&self, address: Address) -> RpcResult<Option<ContractCreator>> {
if !self.has_code(address, None).await? {
return Ok(None);
}
// use binary search from block [1, latest block number] to find the first block where the
// contract was deployed
let mut low = 1;
let mut high = self.eth.block_number()?.saturating_to::<u64>();
let mut num = high;
while low <= high {
let mid = (low + high) / 2;
if self.eth.get_code(address, Some(mid.into())).await?.is_empty() {
// not found in current block, need to search in the later blocks
low = mid + 1;
} else {
// found in current block, try to find a lower block
high = mid - 1;
num = mid;
}
}
let traces = self
.eth
.trace_block_with(
num.into(),
TracingInspectorConfig::default_parity(),
|tx_info, inspector, _, _, _| {
Ok(inspector.into_parity_builder().into_localized_transaction_traces(tx_info))
},
)
.await?
.map(|traces| {
traces
.into_iter()
.flatten()
.map(|tx_trace| {
let trace = tx_trace.trace;
Ok(match (trace.action, trace.result, trace.error) {
(
Action::Create(CreateAction { from: creator, .. }),
Some(TraceOutput::Create(CreateOutput {
address: contract, ..
})),
None,
) if contract == address => Some(ContractCreator {
hash: tx_trace
.transaction_hash
.ok_or_else(|| EthApiError::TransactionNotFound)?,
creator,
}),
_ => None,
})
})
.filter_map(Result::transpose)
.collect::<Result<Vec<_>, EthApiError>>()
})
.transpose()?;
// A contract maybe created and then destroyed in multiple transactions, here we
// return the first found transaction, this behavior is consistent with etherscan's
let found = traces.and_then(|traces| traces.first().cloned());
Ok(found)
}
}