Skip to content

Commit 35c4461

Browse files
authored
fix for no-code panic bug (#408)
1 parent 569e533 commit 35c4461

11 files changed

Lines changed: 426 additions & 45 deletions

File tree

core/src/event/callback_registry.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,32 @@ pub enum CallbackResult {
4646
Trace(Vec<TraceResult>),
4747
}
4848

49+
impl CallbackResult {
50+
/// return the (from_block, to_block, network) entry regardless of variant
51+
pub fn first_metadata(&self) -> Option<(U64, U64, String)> {
52+
match self {
53+
Self::Event(events) => events.first().map(|e| {
54+
(
55+
e.found_in_request.from_block,
56+
e.found_in_request.to_block,
57+
e.tx_information.network.clone(),
58+
)
59+
}),
60+
Self::Trace(traces) => traces.first().map(|t| {
61+
let (fir, tx) = match t {
62+
TraceResult::NativeTransfer { found_in_request, tx_information, .. } => {
63+
(found_in_request, tx_information)
64+
}
65+
TraceResult::Block { found_in_request, tx_information, .. } => {
66+
(found_in_request, tx_information)
67+
}
68+
};
69+
(fir.from_block, fir.to_block, tx.network.clone())
70+
}),
71+
}
72+
}
73+
}
74+
4975
#[derive(Debug, Serialize, Deserialize, Clone)]
5076
pub struct TxInformation {
5177
pub chain_id: u64,
@@ -507,3 +533,128 @@ where
507533
}
508534
}
509535
}
536+
537+
#[cfg(test)]
538+
mod tests {
539+
use super::*;
540+
use alloy::network::AnyRpcBlock;
541+
542+
fn test_tx_information(network: &str) -> TxInformation {
543+
TxInformation {
544+
chain_id: 31337,
545+
network: network.to_string(),
546+
address: Address::ZERO,
547+
block_hash: BlockHash::ZERO,
548+
block_number: 0,
549+
block_timestamp: None,
550+
transaction_hash: TxHash::ZERO,
551+
transaction_index: 0,
552+
log_index: U256::ZERO,
553+
}
554+
}
555+
556+
fn test_found_in_request(from_block: u64, to_block: u64) -> LogFoundInRequest {
557+
LogFoundInRequest { from_block: U64::from(from_block), to_block: U64::from(to_block) }
558+
}
559+
560+
//Minimal valid json for alloy::network::AnyRpcBlock deserialization
561+
const TEST_BLOCK_JSON: &str = r#"{
562+
"number": "0x0",
563+
"hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
564+
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
565+
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
566+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
567+
"transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
568+
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
569+
"receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
570+
"miner": "0x0000000000000000000000000000000000000000",
571+
"difficulty": "0x0",
572+
"extraData": "0x",
573+
"size": "0x0",
574+
"gasLimit": "0x0",
575+
"gasUsed": "0x0",
576+
"timestamp": "0x0",
577+
"transactions": [],
578+
"uncles": []
579+
}"#;
580+
581+
const TEST_LOG_JSON: &str = r#"{
582+
"address": "0x0000000000000000000000000000000000000000",
583+
"topics": [],
584+
"data": "0x",
585+
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
586+
"blockNumber": "0x0",
587+
"transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
588+
"transactionIndex": "0x0",
589+
"logIndex": "0x0",
590+
"removed": false
591+
}"#;
592+
593+
fn test_block() -> AnyRpcBlock {
594+
serde_json::from_str(TEST_BLOCK_JSON).expect("test block JSON should deserialize")
595+
}
596+
597+
fn test_log() -> Log {
598+
serde_json::from_str(TEST_LOG_JSON).expect("test log JSON should deserialize")
599+
}
600+
601+
#[test]
602+
fn first_metadata_event_returns_fields_from_first_entry() {
603+
let event = EventResult {
604+
log: test_log(),
605+
decoded_data: Arc::new(()),
606+
tx_information: test_tx_information("mainnet"),
607+
found_in_request: test_found_in_request(10, 20),
608+
};
609+
let result = CallbackResult::Event(vec![event]);
610+
let (from_block, to_block, network) = result.first_metadata().expect("non-empty batch");
611+
assert_eq!(from_block, U64::from(10));
612+
assert_eq!(to_block, U64::from(20));
613+
assert_eq!(network, "mainnet");
614+
}
615+
616+
#[test]
617+
fn first_metadata_trace_native_transfer_returns_fields() {
618+
let nt = TraceResult::NativeTransfer {
619+
from: Address::ZERO,
620+
to: Address::ZERO,
621+
value: U256::ZERO,
622+
tx_information: test_tx_information("anvil"),
623+
found_in_request: test_found_in_request(5, 15),
624+
};
625+
let result = CallbackResult::Trace(vec![nt]);
626+
let (from_block, to_block, network) = result.first_metadata().expect("non-empty batch");
627+
assert_eq!(from_block, U64::from(5));
628+
assert_eq!(to_block, U64::from(15));
629+
assert_eq!(network, "anvil");
630+
}
631+
632+
//native_transfer_block_consumer always fires trigger_event with a Block-only batch before firing the
633+
// NativeTransfer batch, so first_metadata must
634+
// extract cleanly from a TraceResult::Block without panicking
635+
#[test]
636+
fn first_metadata_trace_block_returns_fields() {
637+
let b = TraceResult::Block {
638+
block: Box::new(test_block()),
639+
tx_information: test_tx_information("polygon"),
640+
found_in_request: test_found_in_request(100, 200),
641+
};
642+
let result = CallbackResult::Trace(vec![b]);
643+
let (from_block, to_block, network) = result.first_metadata().expect("non-empty batch");
644+
assert_eq!(from_block, U64::from(100));
645+
assert_eq!(to_block, U64::from(200));
646+
assert_eq!(network, "polygon");
647+
}
648+
649+
#[test]
650+
fn first_metadata_empty_event_returns_none() {
651+
let result = CallbackResult::Event(vec![]);
652+
assert!(result.first_metadata().is_none());
653+
}
654+
655+
#[test]
656+
fn first_metadata_empty_trace_returns_none() {
657+
let result = CallbackResult::Trace(vec![]);
658+
assert!(result.first_metadata().is_none());
659+
}
660+
}

core/src/indexer/no_code.rs

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -338,48 +338,9 @@ fn no_code_callback(params: Arc<NoCodeCallbackParams>) -> EventCallbacks {
338338
return Ok(());
339339
}
340340

341-
// TODO
342-
// Remove unwrap
343-
let (from_block, to_block) = match &results {
344-
CallbackResult::Event(event) => (
345-
event.first().unwrap().found_in_request.from_block,
346-
event.first().unwrap().found_in_request.to_block,
347-
),
348-
CallbackResult::Trace(event) => {
349-
// Filter to only NativeTransfer events and get the first one
350-
let native_transfer = event
351-
.iter()
352-
.filter_map(|result| match result {
353-
TraceResult::NativeTransfer { found_in_request, .. } => {
354-
Some(found_in_request)
355-
}
356-
TraceResult::Block { .. } => None,
357-
})
358-
.next()
359-
.unwrap();
360-
(native_transfer.from_block, native_transfer.to_block)
361-
}
362-
};
363-
364-
let network = match &results {
365-
CallbackResult::Event(event) => {
366-
event.first().unwrap().tx_information.network.clone()
367-
}
368-
CallbackResult::Trace(event) => {
369-
// Filter to only NativeTransfer events and get the first one
370-
event
371-
.iter()
372-
.filter_map(|result| match result {
373-
TraceResult::NativeTransfer { tx_information, .. } => {
374-
Some(&tx_information.network)
375-
}
376-
TraceResult::Block { .. } => None,
377-
})
378-
.next()
379-
.unwrap()
380-
.clone()
381-
}
382-
};
341+
//guarantees non empty batch
342+
let (from_block, to_block, network) =
343+
results.first_metadata().expect("event_length > 0 guarantees at least one entry");
383344

384345
let mut indexed_count = 0;
385346
let mut sql_bulk_data: Vec<Vec<EthereumSqlTypeWrapper>> = Vec::new();

e2e-tests/src/anvil_setup.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,43 @@ impl AnvilInstance {
360360
Ok(tx_hashes)
361361
}
362362

363+
///send plain eth transfer
364+
pub async fn send_eth_transfer(
365+
&self,
366+
to: &ethers::types::Address,
367+
amount: ethers::types::U256,
368+
) -> Result<String> {
369+
use ethers::middleware::MiddlewareBuilder;
370+
use ethers::providers::{Http, Middleware, Provider};
371+
use ethers::signers::{LocalWallet, Signer};
372+
use ethers::types::TransactionRequest;
373+
374+
let base_provider = Provider::<Http>::try_from(&self.rpc_url)?;
375+
let chain_id = base_provider.get_chainid().await?.as_u64();
376+
377+
let wallet: LocalWallet = ANVIL_DEFAULT_PRIVATE_KEY.parse()?;
378+
let wallet = wallet.with_chain_id(chain_id);
379+
let signer_address = wallet.address();
380+
let provider = base_provider.with_signer(wallet);
381+
382+
let nonce = provider.get_transaction_count(signer_address, None).await?;
383+
384+
let tx = TransactionRequest {
385+
from: Some(signer_address),
386+
to: Some((*to).into()),
387+
data: None,
388+
gas: Some(21000u64.into()),
389+
nonce: Some(nonce),
390+
gas_price: Some(20000000000u128.into()),
391+
value: Some(amount),
392+
chain_id: None,
393+
};
394+
395+
let pending = provider.send_transaction(tx, None).await?;
396+
let tx_hash = format!("{:?}", pending.tx_hash()).to_lowercase();
397+
Ok(tx_hash)
398+
}
399+
363400
/// Get transaction receipt.
364401
#[allow(dead_code)]
365402
pub async fn get_receipt(&self, tx_hash: &str) -> Result<TxReceipt> {

e2e-tests/src/rindexer_client.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,11 @@ impl RindexerInstance {
422422
csv: crate::test_suite::CsvConfig { enabled: true },
423423
clickhouse: None,
424424
},
425-
native_transfers: crate::test_suite::NativeTransfersConfig { enabled: false },
425+
native_transfers: crate::test_suite::NativeTransfersConfig {
426+
enabled: false,
427+
networks: None,
428+
generate_csv: None,
429+
},
426430
contracts: vec![],
427431
}
428432
}

e2e-tests/src/test_suite.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ pub struct CsvConfig {
7171
#[derive(Debug, serde::Serialize, serde::Deserialize)]
7272
pub struct NativeTransfersConfig {
7373
pub enabled: bool,
74+
#[serde(skip_serializing_if = "Option::is_none")]
75+
pub networks: Option<Vec<NativeTransferNetworkDetail>>,
76+
#[serde(skip_serializing_if = "Option::is_none")]
77+
pub generate_csv: Option<bool>,
78+
}
79+
80+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
81+
pub struct NativeTransferNetworkDetail {
82+
pub network: String,
83+
#[serde(skip_serializing_if = "Option::is_none")]
84+
pub start_block: Option<String>,
85+
#[serde(skip_serializing_if = "Option::is_none")]
86+
pub end_block: Option<String>,
7487
}
7588

7689
#[derive(Debug, serde::Serialize, serde::Deserialize)]

e2e-tests/src/tests/direct_rpc.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ fn build_direct_rpc_config(
111111
csv: CsvConfig { enabled: true },
112112
clickhouse: None,
113113
},
114-
native_transfers: NativeTransfersConfig { enabled: false },
114+
native_transfers: NativeTransfersConfig {
115+
enabled: false,
116+
networks: None,
117+
generate_csv: None,
118+
},
115119
contracts: vec![ContractConfig {
116120
name: "ERC20".to_string(),
117121
details: vec![ContractDetail {

e2e-tests/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod health_assertions;
1010
pub mod historic_indexing;
1111
pub mod live_indexing;
1212
pub mod multi_network;
13+
pub mod native_transfer;
1314
pub mod postgres_e2e;
1415
pub mod reorg_e2e;
1516
pub mod restart_checkpoint;

e2e-tests/src/tests/multi_network.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,11 @@ fn build_multi_network_config(
268268
csv: CsvConfig { enabled: true },
269269
clickhouse: None,
270270
},
271-
native_transfers: NativeTransfersConfig { enabled: false },
271+
native_transfers: NativeTransfersConfig {
272+
enabled: false,
273+
networks: None,
274+
generate_csv: None,
275+
},
272276
contracts: vec![
273277
ContractConfig {
274278
name: "RocketPoolETH".to_string(),

0 commit comments

Comments
 (0)