1use super::{CreateTestsArgs, ReportMode, RunIgnored, TestCriteriaOverride};
5use crate::blocks::{ElectionProof, Ticket, Tipset};
6use crate::chain::ChainStore;
7use crate::db::car::ManyCar;
8use crate::eth::{EthChainId as EthChainIdType, SAFE_EPOCH_DELAY};
9use crate::lotus_json::HasLotusJson;
10use crate::message::{Message as _, SignedMessage};
11use crate::rpc::FilterList;
12use crate::rpc::auth::AuthNewParams;
13use crate::rpc::beacon::BeaconGetEntry;
14use crate::rpc::eth::{
15 BlockNumberOrHash, EthInt64, ExtBlockNumberOrHash, ExtPredefined, Predefined,
16 new_eth_tx_from_signed_message, types::*,
17};
18use crate::rpc::gas::GasEstimateGasLimit;
19use crate::rpc::miner::BlockTemplate;
20use crate::rpc::misc::ActorEventFilter;
21use crate::rpc::state::StateGetAllClaims;
22use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup};
23use crate::rpc::{Permission, prelude::*};
24use crate::shim::actors::MarketActorStateLoad as _;
25use crate::shim::actors::market;
26use crate::shim::executor::Receipt;
27use crate::shim::sector::SectorSize;
28use crate::shim::{
29 address::{Address, Protocol},
30 crypto::Signature,
31 econ::TokenAmount,
32 message::{METHOD_SEND, Message},
33 state_tree::StateTree,
34};
35use crate::state_manager::StateManager;
36use crate::tool::offline_server::server::handle_chain_config;
37use crate::tool::subcommands::api_cmd::NetworkChain;
38use crate::tool::subcommands::api_cmd::report::ReportBuilder;
39use crate::tool::subcommands::api_cmd::state_decode_params_tests::create_all_state_decode_params_tests;
40use crate::utils::proofs_api::{self, ensure_proof_params_downloaded};
41use crate::{Config, rpc};
42use ahash::HashMap;
43use bls_signatures::Serialize as _;
44use chrono::Utc;
45use cid::Cid;
46use fil_actors_shared::fvm_ipld_bitfield::BitField;
47use fil_actors_shared::v10::runtime::DomainSeparationTag;
48use futures::stream::FuturesUnordered;
49use futures::stream::StreamExt as _;
50use fvm_ipld_blockstore::Blockstore;
51use ipld_core::ipld::Ipld;
52use itertools::Itertools as _;
53use jsonrpsee::types::ErrorCode;
54use libp2p::PeerId;
55use num_traits::Signed;
56use serde::de::DeserializeOwned;
57use serde::{Deserialize, Serialize};
58use serde_json::Value;
59use similar::{ChangeTag, TextDiff};
60use std::path::Path;
61use std::time::Instant;
62use std::{
63 path::PathBuf,
64 str::FromStr,
65 sync::{Arc, LazyLock},
66 time::Duration,
67};
68use tokio::sync::Semaphore;
69use tracing::debug;
70
71const COLLECTION_SAMPLE_SIZE: usize = 5;
72
73static KNOWN_CALIBNET_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
76 crate::shim::address::Network::Testnet
77 .parse_address("t1c4dkec3qhrnrsa4mccy7qntkyq2hhsma4sq7lui")
78 .unwrap()
79 .into()
80});
81
82static KNOWN_EMPTY_CALIBNET_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
84 crate::shim::address::Network::Testnet
85 .parse_address("t1qb2x5qctp34rxd7ucl327h5ru6aazj2heno7x5y")
86 .unwrap()
87 .into()
88});
89
90const TICKET_QUALITY_GREEDY: f64 = 0.9;
91const TICKET_QUALITY_OPTIMAL: f64 = 0.8;
92const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";
93const MINER_ADDRESS: Address = Address::new_id(78216); const ACCOUNT_ADDRESS: Address = Address::new_id(1234); const EVM_ADDRESS: &str = "t410fbqoynu2oi2lxam43knqt6ordiowm2ywlml27z4i";
97
98#[derive(
100 Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, strum::Display,
101)]
102#[serde(rename_all = "snake_case")]
103pub enum TestSummary {
104 MissingMethod,
106 Rejected(String),
108 NotJsonRPC,
110 InfraError,
112 BadJson,
114 CustomCheckFailed,
116 Timeout,
118 Valid,
120}
121
122impl TestSummary {
123 fn from_err(err: &rpc::ClientError) -> Self {
124 match err {
125 rpc::ClientError::Call(it) => match it.code().into() {
126 ErrorCode::MethodNotFound => Self::MissingMethod,
127 _ => Self::Rejected(it.message().to_string()),
128 },
129 rpc::ClientError::ParseError(_) => Self::NotJsonRPC,
130 rpc::ClientError::RequestTimeout => Self::Timeout,
131 rpc::ClientError::Transport(_)
132 | rpc::ClientError::RestartNeeded(_)
133 | rpc::ClientError::InvalidSubscriptionId
134 | rpc::ClientError::InvalidRequestId(_)
135 | rpc::ClientError::Custom(_)
136 | rpc::ClientError::HttpNotImplemented
137 | rpc::ClientError::EmptyBatchRequest(_)
138 | rpc::ClientError::RegisterMethod(_) => Self::InfraError,
139 _ => unimplemented!(),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct TestDump {
147 pub request: rpc::Request,
148 pub forest_response: Result<Value, String>,
149 pub lotus_response: Result<Value, String>,
150}
151
152impl std::fmt::Display for TestDump {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 writeln!(f, "Request dump: {:?}", self.request)?;
155 writeln!(f, "Request params JSON: {}", self.request.params)?;
156 let (forest_response, lotus_response) = (
157 self.forest_response
158 .as_ref()
159 .ok()
160 .and_then(|v| serde_json::to_string_pretty(v).ok()),
161 self.lotus_response
162 .as_ref()
163 .ok()
164 .and_then(|v| serde_json::to_string_pretty(v).ok()),
165 );
166 if let Some(forest_response) = &forest_response
167 && let Some(lotus_response) = &lotus_response
168 {
169 let diff = TextDiff::from_lines(forest_response, lotus_response);
170 let mut print_diff = Vec::new();
171 for change in diff.iter_all_changes() {
172 let sign = match change.tag() {
173 ChangeTag::Delete => "-",
174 ChangeTag::Insert => "+",
175 ChangeTag::Equal => " ",
176 };
177 print_diff.push(format!("{sign}{change}"));
178 }
179 writeln!(f, "Forest response: {forest_response}")?;
180 writeln!(f, "Lotus response: {lotus_response}")?;
181 writeln!(f, "Diff: {}", print_diff.join("\n"))?;
182 } else {
183 if let Some(forest_response) = &forest_response {
184 writeln!(f, "Forest response: {forest_response}")?;
185 }
186 if let Some(lotus_response) = &lotus_response {
187 writeln!(f, "Lotus response: {lotus_response}")?;
188 }
189 };
190 Ok(())
191 }
192}
193
194pub struct TestResult {
196 pub forest_status: TestSummary,
198 pub lotus_status: TestSummary,
200 pub test_dump: Option<TestDump>,
202 pub duration: Duration,
204}
205
206pub(super) enum PolicyOnRejected {
207 Fail,
208 Pass,
209 PassWithIdenticalError,
210 PassWithIdenticalErrorCaseInsensitive,
211 PassWithQuasiIdenticalError,
214}
215
216pub(super) enum SortPolicy {
217 All,
219}
220
221pub(super) struct RpcTest {
222 pub request: rpc::Request,
223 pub check_syntax: Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>,
224 pub check_semantics: Arc<dyn Fn(serde_json::Value, serde_json::Value) -> bool + Send + Sync>,
225 pub ignore: Option<&'static str>,
226 pub policy_on_rejected: PolicyOnRejected,
227 pub sort_policy: Option<SortPolicy>,
228}
229
230fn sort_json(value: &mut Value) {
231 match value {
232 Value::Array(arr) => {
233 for v in arr.iter_mut() {
234 sort_json(v);
235 }
236 arr.sort_by_key(|a| a.to_string());
237 }
238 Value::Object(obj) => {
239 let mut sorted_map: serde_json::Map<String, Value> = serde_json::Map::new();
240 let mut keys: Vec<String> = obj.keys().cloned().collect();
241 keys.sort();
242 for k in keys {
243 let mut v = obj.remove(&k).unwrap();
244 sort_json(&mut v);
245 sorted_map.insert(k, v);
246 }
247 *obj = sorted_map;
248 }
249 _ => (),
250 }
251}
252
253impl RpcTest {
257 fn basic<T>(request: rpc::Request<T>) -> Self
260 where
261 T: HasLotusJson,
262 {
263 Self::basic_raw(request.map_ty::<T::LotusJson>())
264 }
265 fn basic_raw<T: DeserializeOwned>(request: rpc::Request<T>) -> Self {
267 Self {
268 request: request.map_ty(),
269 check_syntax: Arc::new(|it| match serde_json::from_value::<T>(it) {
270 Ok(_) => true,
271 Err(e) => {
272 debug!(?e);
273 false
274 }
275 }),
276 check_semantics: Arc::new(|_, _| true),
277 ignore: None,
278 policy_on_rejected: PolicyOnRejected::Fail,
279 sort_policy: None,
280 }
281 }
282 fn validate<T: HasLotusJson>(
285 request: rpc::Request<T>,
286 validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
287 ) -> Self {
288 Self::validate_raw(request.map_ty::<T::LotusJson>(), move |l, r| {
289 validate(T::from_lotus_json(l), T::from_lotus_json(r))
290 })
291 }
292 fn validate_raw<T: DeserializeOwned>(
294 request: rpc::Request<T>,
295 validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
296 ) -> Self {
297 Self {
298 request: request.map_ty(),
299 check_syntax: Arc::new(|value| match serde_json::from_value::<T>(value) {
300 Ok(_) => true,
301 Err(e) => {
302 debug!("{e}");
303 false
304 }
305 }),
306 check_semantics: Arc::new(move |forest_json, lotus_json| {
307 match (
308 serde_json::from_value::<T>(forest_json),
309 serde_json::from_value::<T>(lotus_json),
310 ) {
311 (Ok(forest), Ok(lotus)) => validate(forest, lotus),
312 (forest, lotus) => {
313 if let Err(e) = forest {
314 debug!("[forest] invalid json: {e}");
315 }
316 if let Err(e) = lotus {
317 debug!("[lotus] invalid json: {e}");
318 }
319 false
320 }
321 }
322 }),
323 ignore: None,
324 policy_on_rejected: PolicyOnRejected::Fail,
325 sort_policy: None,
326 }
327 }
328 pub(crate) fn identity<T: PartialEq + HasLotusJson>(request: rpc::Request<T>) -> RpcTest {
331 Self::validate(request, |forest, lotus| forest == lotus)
332 }
333
334 fn ignore(mut self, msg: &'static str) -> Self {
335 self.ignore = Some(msg);
336 self
337 }
338
339 fn policy_on_rejected(mut self, policy: PolicyOnRejected) -> Self {
340 self.policy_on_rejected = policy;
341 self
342 }
343
344 fn sort_policy(mut self, policy: SortPolicy) -> Self {
345 self.sort_policy = Some(policy);
346 self
347 }
348
349 async fn run(&self, forest: &rpc::Client, lotus: &rpc::Client) -> TestResult {
350 let start = Instant::now();
351 let forest_resp = forest.call(self.request.clone()).await;
352 let forest_response = forest_resp.as_ref().map_err(|e| e.to_string()).cloned();
353 let lotus_resp = lotus.call(self.request.clone()).await;
354 let lotus_response = lotus_resp.as_ref().map_err(|e| e.to_string()).cloned();
355
356 let (forest_status, lotus_status) = match (forest_resp, lotus_resp) {
357 (Ok(forest), Ok(lotus))
358 if (self.check_syntax)(forest.clone()) && (self.check_syntax)(lotus.clone()) =>
359 {
360 let (forest, lotus) = if self.sort_policy.is_some() {
361 let mut sorted_forest = forest.clone();
362 sort_json(&mut sorted_forest);
363 let mut sorted_lotus = lotus.clone();
364 sort_json(&mut sorted_lotus);
365 (sorted_forest, sorted_lotus)
366 } else {
367 (forest, lotus)
368 };
369 let forest_status = if (self.check_semantics)(forest, lotus) {
370 TestSummary::Valid
371 } else {
372 TestSummary::CustomCheckFailed
373 };
374 (forest_status, TestSummary::Valid)
375 }
376 (forest_resp, lotus_resp) => {
377 let forest_status = forest_resp.map_or_else(
378 |e| TestSummary::from_err(&e),
379 |value| {
380 if (self.check_syntax)(value) {
381 TestSummary::Valid
382 } else {
383 TestSummary::BadJson
384 }
385 },
386 );
387 let lotus_status = lotus_resp.map_or_else(
388 |e| TestSummary::from_err(&e),
389 |value| {
390 if (self.check_syntax)(value) {
391 TestSummary::Valid
392 } else {
393 TestSummary::BadJson
394 }
395 },
396 );
397
398 (forest_status, lotus_status)
399 }
400 };
401
402 TestResult {
403 forest_status,
404 lotus_status,
405 test_dump: Some(TestDump {
406 request: self.request.clone(),
407 forest_response,
408 lotus_response,
409 }),
410 duration: start.elapsed(),
411 }
412 }
413}
414
415fn common_tests() -> Vec<RpcTest> {
416 vec![
417 RpcTest::validate(Version::request(()).unwrap(), |forest, lotus| {
419 forest.api_version == lotus.api_version && forest.block_delay == lotus.block_delay
420 }),
421 RpcTest::basic(StartTime::request(()).unwrap()),
422 RpcTest::basic(Session::request(()).unwrap()),
423 ]
424}
425
426fn chain_tests() -> Vec<RpcTest> {
427 vec![
428 RpcTest::basic(ChainHead::request(()).unwrap()),
429 RpcTest::identity(ChainGetGenesis::request(()).unwrap()),
430 ]
431}
432
433fn chain_tests_with_tipset<DB: Blockstore>(
434 store: &Arc<DB>,
435 tipset: &Tipset,
436) -> anyhow::Result<Vec<RpcTest>> {
437 let mut tests = vec![
438 RpcTest::identity(ChainGetTipSetByHeight::request((
439 tipset.epoch(),
440 Default::default(),
441 ))?),
442 RpcTest::identity(ChainGetTipSetAfterHeight::request((
443 tipset.epoch(),
444 Default::default(),
445 ))?),
446 RpcTest::identity(ChainGetTipSet::request((tipset.key().clone().into(),))?),
447 RpcTest::identity(ChainGetPath::request((
448 tipset.key().clone(),
449 tipset.parents().clone(),
450 ))?),
451 RpcTest::identity(ChainGetMessagesInTipset::request((tipset
452 .key()
453 .clone()
454 .into(),))?),
455 RpcTest::identity(ChainTipSetWeight::request((tipset.key().into(),))?),
456 RpcTest::basic(ChainGetFinalizedTipset::request(())?),
457 ];
458
459 for block in tipset.block_headers() {
460 let block_cid = *block.cid();
461 tests.extend([
462 RpcTest::identity(ChainReadObj::request((block_cid,))?),
463 RpcTest::identity(ChainHasObj::request((block_cid,))?),
464 RpcTest::identity(ChainGetBlock::request((block_cid,))?),
465 RpcTest::identity(ChainGetBlockMessages::request((block_cid,))?),
466 RpcTest::identity(ChainGetParentMessages::request((block_cid,))?),
467 RpcTest::identity(ChainGetParentReceipts::request((block_cid,))?),
468 RpcTest::identity(ChainStatObj::request((block.messages, None))?),
469 RpcTest::identity(ChainStatObj::request((
470 block.messages,
471 Some(block.messages),
472 ))?),
473 ]);
474
475 let (bls_messages, secp_messages) = crate::chain::store::block_messages(&store, block)?;
476 for msg_cid in sample_message_cids(bls_messages.iter(), secp_messages.iter()) {
477 tests.extend([RpcTest::identity(ChainGetMessage::request((msg_cid,))?)]);
478 }
479
480 for receipt in Receipt::get_receipts(store, block.message_receipts)? {
481 if let Some(events_root) = receipt.events_root() {
482 tests.extend([RpcTest::identity(ChainGetEvents::request((events_root,))?)
483 .sort_policy(SortPolicy::All)]);
484 }
485 }
486 }
487
488 Ok(tests)
489}
490
491fn auth_tests() -> anyhow::Result<Vec<RpcTest>> {
492 Ok(vec![
494 RpcTest::basic(AuthNew::request((
495 AuthNewParams::process_perms(Permission::Admin.to_string())?,
496 None,
497 ))?),
498 RpcTest::basic(AuthNew::request((
499 AuthNewParams::process_perms(Permission::Sign.to_string())?,
500 None,
501 ))?),
502 RpcTest::basic(AuthNew::request((
503 AuthNewParams::process_perms(Permission::Write.to_string())?,
504 None,
505 ))?),
506 RpcTest::basic(AuthNew::request((
507 AuthNewParams::process_perms(Permission::Read.to_string())?,
508 None,
509 ))?),
510 ])
511}
512
513fn mpool_tests() -> Vec<RpcTest> {
514 vec![
515 RpcTest::identity(MpoolGetNonce::request((*KNOWN_CALIBNET_ADDRESS,)).unwrap()),
516 RpcTest::identity(MpoolGetNonce::request((*KNOWN_EMPTY_CALIBNET_ADDRESS,)).unwrap())
525 .policy_on_rejected(PolicyOnRejected::Pass),
526 RpcTest::basic(MpoolPending::request((ApiTipsetKey(None),)).unwrap()),
527 RpcTest::basic(MpoolSelect::request((ApiTipsetKey(None), TICKET_QUALITY_GREEDY)).unwrap()),
528 RpcTest::basic(MpoolSelect::request((ApiTipsetKey(None), TICKET_QUALITY_OPTIMAL)).unwrap())
529 .ignore("https://github.com/ChainSafe/forest/issues/4490"),
530 ]
531}
532
533fn mpool_tests_with_tipset(tipset: &Tipset) -> Vec<RpcTest> {
534 vec![
535 RpcTest::basic(MpoolPending::request((tipset.key().into(),)).unwrap()),
536 RpcTest::basic(MpoolSelect::request((tipset.key().into(), TICKET_QUALITY_GREEDY)).unwrap()),
537 RpcTest::basic(
538 MpoolSelect::request((tipset.key().into(), TICKET_QUALITY_OPTIMAL)).unwrap(),
539 )
540 .ignore("https://github.com/ChainSafe/forest/issues/4490"),
541 ]
542}
543
544fn net_tests() -> Vec<RpcTest> {
545 vec![
548 RpcTest::basic(NetAddrsListen::request(()).unwrap()),
549 RpcTest::basic(NetPeers::request(()).unwrap()),
550 RpcTest::identity(NetListening::request(()).unwrap()),
551 RpcTest::basic(NetAgentVersion::request((PeerId::random().to_string(),)).unwrap())
553 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
554 RpcTest::basic(NetFindPeer::request((PeerId::random().to_string(),)).unwrap())
555 .policy_on_rejected(PolicyOnRejected::Pass)
556 .ignore("It times out in lotus when peer not found"),
557 RpcTest::basic(NetInfo::request(()).unwrap())
558 .ignore("Not implemented in Lotus. Why do we even have this method?"),
559 RpcTest::basic(NetAutoNatStatus::request(()).unwrap()),
560 RpcTest::identity(NetVersion::request(()).unwrap()),
561 RpcTest::identity(NetProtectAdd::request((vec![PeerId::random().to_string()],)).unwrap()),
562 RpcTest::identity(
563 NetProtectRemove::request((vec![PeerId::random().to_string()],)).unwrap(),
564 ),
565 RpcTest::basic(NetProtectList::request(()).unwrap()),
566 ]
567}
568
569fn node_tests() -> Vec<RpcTest> {
570 vec![
571 ]
575}
576
577fn event_tests_with_tipset<DB: Blockstore>(_store: &Arc<DB>, tipset: &Tipset) -> Vec<RpcTest> {
578 let epoch = tipset.epoch();
579 vec![
580 RpcTest::identity(GetActorEventsRaw::request((None,)).unwrap()),
581 RpcTest::identity(
582 GetActorEventsRaw::request((Some(ActorEventFilter {
583 addresses: vec![],
584 fields: Default::default(),
585 from_height: Some(epoch),
586 to_height: Some(epoch),
587 tipset_key: None,
588 }),))
589 .unwrap(),
590 )
591 .sort_policy(SortPolicy::All),
592 RpcTest::identity(
593 GetActorEventsRaw::request((Some(ActorEventFilter {
594 addresses: vec![],
595 fields: Default::default(),
596 from_height: Some(epoch - 100),
597 to_height: Some(epoch),
598 tipset_key: None,
599 }),))
600 .unwrap(),
601 )
602 .sort_policy(SortPolicy::All),
603 RpcTest::identity(
604 GetActorEventsRaw::request((Some(ActorEventFilter {
605 addresses: vec![],
606 fields: Default::default(),
607 from_height: None,
608 to_height: None,
609 tipset_key: Some(tipset.key().clone().into()),
610 }),))
611 .unwrap(),
612 )
613 .sort_policy(SortPolicy::All),
614 RpcTest::identity(
615 GetActorEventsRaw::request((Some(ActorEventFilter {
616 addresses: vec![
617 Address::from_str("t410fvtakbtytk4otbnfymn4zn5ow252nj7lcpbtersq")
618 .unwrap()
619 .into(),
620 ],
621 fields: Default::default(),
622 from_height: Some(epoch - 100),
623 to_height: Some(epoch),
624 tipset_key: None,
625 }),))
626 .unwrap(),
627 )
628 .sort_policy(SortPolicy::All),
629 {
630 use std::collections::BTreeMap;
631
632 use base64::{Engine, prelude::BASE64_STANDARD};
633
634 use crate::lotus_json::LotusJson;
635 use crate::rpc::misc::ActorEventBlock;
636
637 let topic = BASE64_STANDARD
638 .decode("0Gprf0kYSUs3GSF9GAJ4bB9REqbB2I/iz+wAtFhPauw=")
639 .unwrap();
640 let mut fields: BTreeMap<String, Vec<ActorEventBlock>> = Default::default();
641 fields.insert(
642 "t1".into(),
643 vec![ActorEventBlock {
644 codec: 85,
645 value: LotusJson(topic),
646 }],
647 );
648 RpcTest::identity(
649 GetActorEventsRaw::request((Some(ActorEventFilter {
650 addresses: vec![],
651 fields,
652 from_height: Some(epoch - 100),
653 to_height: Some(epoch),
654 tipset_key: None,
655 }),))
656 .unwrap(),
657 )
658 .sort_policy(SortPolicy::All)
659 },
660 ]
661}
662
663fn miner_tests_with_tipset<DB: Blockstore>(
664 store: &Arc<DB>,
665 tipset: &Tipset,
666 miner_address: Option<Address>,
667) -> anyhow::Result<Vec<RpcTest>> {
668 let Some(miner_address) = miner_address else {
670 return Ok(vec![]);
671 };
672
673 let mut tests = Vec::new();
674 for block in tipset.block_headers() {
675 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
676 tests.push(miner_create_block_test(
677 miner_address,
678 tipset,
679 bls_messages,
680 secp_messages,
681 ));
682 }
683 tests.push(miner_create_block_no_messages_test(miner_address, tipset));
684 Ok(tests)
685}
686
687fn miner_create_block_test(
688 miner: Address,
689 tipset: &Tipset,
690 bls_messages: Vec<Message>,
691 secp_messages: Vec<SignedMessage>,
692) -> RpcTest {
693 let priv_key = bls_signatures::PrivateKey::generate(&mut crate::utils::rand::forest_rng());
695 let signed_bls_msgs = bls_messages
696 .into_iter()
697 .map(|message| {
698 let sig = priv_key.sign(message.cid().to_bytes());
699 SignedMessage {
700 message,
701 signature: Signature::new_bls(sig.as_bytes().to_vec()),
702 }
703 })
704 .collect_vec();
705
706 let block_template = BlockTemplate {
707 miner,
708 parents: tipset.parents().to_owned(),
709 ticket: Ticket::default(),
710 eproof: ElectionProof::default(),
711 beacon_values: tipset.block_headers().first().beacon_entries.to_owned(),
712 messages: [signed_bls_msgs, secp_messages].concat(),
713 epoch: tipset.epoch(),
714 timestamp: tipset.min_timestamp(),
715 winning_post_proof: Vec::default(),
716 };
717 RpcTest::identity(MinerCreateBlock::request((block_template,)).unwrap())
718}
719
720fn miner_create_block_no_messages_test(miner: Address, tipset: &Tipset) -> RpcTest {
721 let block_template = BlockTemplate {
722 miner,
723 parents: tipset.parents().to_owned(),
724 ticket: Ticket::default(),
725 eproof: ElectionProof::default(),
726 beacon_values: tipset.block_headers().first().beacon_entries.to_owned(),
727 messages: Vec::default(),
728 epoch: tipset.epoch(),
729 timestamp: tipset.min_timestamp(),
730 winning_post_proof: Vec::default(),
731 };
732 RpcTest::identity(MinerCreateBlock::request((block_template,)).unwrap())
733}
734
735fn state_tests_with_tipset<DB: Blockstore>(
736 store: &Arc<DB>,
737 tipset: &Tipset,
738) -> anyhow::Result<Vec<RpcTest>> {
739 let mut tests = vec![
740 RpcTest::identity(StateNetworkName::request(())?),
741 RpcTest::identity(StateGetNetworkParams::request(())?),
742 RpcTest::identity(StateMinerInitialPledgeForSector::request((
743 1,
744 SectorSize::_32GiB,
745 1024,
746 tipset.key().into(),
747 ))?),
748 RpcTest::identity(StateGetActor::request((
749 Address::SYSTEM_ACTOR,
750 tipset.key().into(),
751 ))?),
752 RpcTest::identity(StateGetRandomnessFromTickets::request((
753 DomainSeparationTag::ElectionProofProduction as i64,
754 tipset.epoch(),
755 "dead beef".as_bytes().to_vec(),
756 tipset.key().into(),
757 ))?),
758 RpcTest::identity(StateGetRandomnessDigestFromTickets::request((
759 tipset.epoch(),
760 tipset.key().into(),
761 ))?),
762 RpcTest::identity(StateGetRandomnessFromBeacon::request((
763 DomainSeparationTag::ElectionProofProduction as i64,
764 tipset.epoch(),
765 "dead beef".as_bytes().to_vec(),
766 tipset.key().into(),
767 ))?),
768 RpcTest::identity(StateGetRandomnessDigestFromBeacon::request((
769 tipset.epoch(),
770 tipset.key().into(),
771 ))?),
772 RpcTest::identity(StateLookupID::request((
774 Address::new_id(0xdeadbeef),
775 tipset.key().into(),
776 ))?),
777 RpcTest::identity(StateVerifiedRegistryRootKey::request((tipset
778 .key()
779 .into(),))?),
780 RpcTest::identity(StateVerifierStatus::request((
781 Address::VERIFIED_REGISTRY_ACTOR,
782 tipset.key().into(),
783 ))?),
784 RpcTest::identity(StateNetworkVersion::request((tipset.key().into(),))?),
785 RpcTest::identity(StateListMiners::request((tipset.key().into(),))?),
786 RpcTest::identity(StateListActors::request((tipset.key().into(),))?),
787 RpcTest::identity(MsigGetAvailableBalance::request((
788 Address::new_id(18101), tipset.key().into(),
790 ))?),
791 RpcTest::identity(MsigGetPending::request((
792 Address::new_id(18101), tipset.key().into(),
794 ))?),
795 RpcTest::identity(MsigGetVested::request((
796 Address::new_id(18101), tipset.parents().into(),
798 tipset.key().into(),
799 ))?),
800 RpcTest::identity(MsigGetVestingSchedule::request((
801 Address::new_id(18101), tipset.key().into(),
803 ))?),
804 RpcTest::identity(BeaconGetEntry::request((tipset.epoch(),))?),
805 RpcTest::identity(StateGetBeaconEntry::request((tipset.epoch(),))?),
806 RpcTest::identity(StateVerifiedClientStatus::request((
810 Address::VERIFIED_REGISTRY_ACTOR,
811 tipset.key().into(),
812 ))?),
813 RpcTest::identity(StateVerifiedClientStatus::request((
814 Address::DATACAP_TOKEN_ACTOR,
815 tipset.key().into(),
816 ))?),
817 RpcTest::identity(StateDealProviderCollateralBounds::request((
818 1,
819 true,
820 tipset.key().into(),
821 ))?),
822 RpcTest::identity(StateCirculatingSupply::request((tipset.key().into(),))?),
823 RpcTest::identity(StateVMCirculatingSupplyInternal::request((tipset
824 .key()
825 .into(),))?),
826 RpcTest::identity(StateMarketParticipants::request((tipset.key().into(),))?),
827 RpcTest::identity(StateMarketDeals::request((tipset.key().into(),))?),
828 RpcTest::identity(StateSectorPreCommitInfo::request((
829 Default::default(), u16::MAX as _,
831 tipset.key().into(),
832 ))?)
833 .policy_on_rejected(PolicyOnRejected::Pass),
834 RpcTest::identity(StateSectorGetInfo::request((
835 Default::default(), u16::MAX as _, tipset.key().into(),
838 ))?)
839 .policy_on_rejected(PolicyOnRejected::Pass),
840 RpcTest::identity(StateGetAllocationIdForPendingDeal::request((
841 u16::MAX as _, tipset.key().into(),
843 ))?),
844 RpcTest::identity(StateGetAllocationForPendingDeal::request((
845 u16::MAX as _, tipset.key().into(),
847 ))?),
848 RpcTest::identity(StateCompute::request((
849 tipset.epoch(),
850 vec![],
851 tipset.key().into(),
852 ))?),
853 ];
854
855 tests.extend(read_state_api_tests(tipset)?);
856 tests.extend(create_all_state_decode_params_tests(tipset)?);
857
858 for &pending_deal_id in
859 StateGetAllocationIdForPendingDeal::get_allocations_for_pending_deals(store, tipset)?
860 .keys()
861 .take(COLLECTION_SAMPLE_SIZE)
862 {
863 tests.extend([
864 RpcTest::identity(StateGetAllocationIdForPendingDeal::request((
865 pending_deal_id,
866 tipset.key().into(),
867 ))?),
868 RpcTest::identity(StateGetAllocationForPendingDeal::request((
869 pending_deal_id,
870 tipset.key().into(),
871 ))?),
872 ]);
873 }
874
875 let (deals, deals_map) = {
877 let state = StateTree::new_from_root(store.clone(), tipset.parent_state())?;
878 let actor = state.get_required_actor(&Address::MARKET_ACTOR)?;
879 let market_state = market::State::load(&store, actor.code, actor.state)?;
880 let proposals = market_state.proposals(&store)?;
881 let mut deals = vec![];
882 let mut deals_map = HashMap::default();
883 proposals.for_each(|deal_id, deal_proposal| {
884 deals.push(deal_id);
885 deals_map.insert(deal_id, deal_proposal);
886 Ok(())
887 })?;
888 (deals, deals_map)
889 };
890
891 for deal in deals.into_iter().take(COLLECTION_SAMPLE_SIZE) {
893 tests.push(RpcTest::identity(StateMarketStorageDeal::request((
894 deal,
895 tipset.key().into(),
896 ))?));
897 }
898
899 for block in tipset.block_headers() {
900 tests.extend([
901 RpcTest::identity(StateMinerAllocated::request((
902 block.miner_address,
903 tipset.key().into(),
904 ))?),
905 RpcTest::identity(StateMinerActiveSectors::request((
906 block.miner_address,
907 tipset.key().into(),
908 ))?),
909 RpcTest::identity(StateLookupID::request((
910 block.miner_address,
911 tipset.key().into(),
912 ))?),
913 RpcTest::identity(StateLookupRobustAddress::request((
914 block.miner_address,
915 tipset.key().into(),
916 ))?),
917 RpcTest::identity(StateMinerSectors::request((
918 block.miner_address,
919 None,
920 tipset.key().into(),
921 ))?),
922 RpcTest::identity(StateMinerPartitions::request((
923 block.miner_address,
924 0,
925 tipset.key().into(),
926 ))?),
927 RpcTest::identity(StateMarketBalance::request((
928 block.miner_address,
929 tipset.key().into(),
930 ))?),
931 RpcTest::identity(StateMinerInfo::request((
932 block.miner_address,
933 tipset.key().into(),
934 ))?),
935 RpcTest::identity(StateMinerPower::request((
936 block.miner_address,
937 tipset.key().into(),
938 ))?),
939 RpcTest::identity(StateMinerDeadlines::request((
940 block.miner_address,
941 tipset.key().into(),
942 ))?),
943 RpcTest::identity(StateMinerProvingDeadline::request((
944 block.miner_address,
945 tipset.key().into(),
946 ))?),
947 RpcTest::identity(StateMinerAvailableBalance::request((
948 block.miner_address,
949 tipset.key().into(),
950 ))?),
951 RpcTest::identity(StateMinerFaults::request((
952 block.miner_address,
953 tipset.key().into(),
954 ))?),
955 RpcTest::identity(MinerGetBaseInfo::request((
956 block.miner_address,
957 block.epoch,
958 tipset.key().into(),
959 ))?),
960 RpcTest::identity(StateMinerRecoveries::request((
961 block.miner_address,
962 tipset.key().into(),
963 ))?),
964 RpcTest::identity(StateMinerSectorCount::request((
965 block.miner_address,
966 tipset.key().into(),
967 ))?),
968 RpcTest::identity(StateGetClaims::request((
969 block.miner_address,
970 tipset.key().into(),
971 ))?),
972 RpcTest::identity(StateGetAllClaims::request((tipset.key().into(),))?),
973 RpcTest::identity(StateGetAllAllocations::request((tipset.key().into(),))?),
974 RpcTest::identity(StateSectorPreCommitInfo::request((
975 block.miner_address,
976 u16::MAX as _, tipset.key().into(),
978 ))?)
979 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
980 RpcTest::identity(StateSectorGetInfo::request((
981 block.miner_address,
982 u16::MAX as _, tipset.key().into(),
984 ))?)
985 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
986 ]);
987 for claim_id in StateGetClaims::get_claims(store, &block.miner_address, tipset)?
988 .keys()
989 .take(COLLECTION_SAMPLE_SIZE)
990 {
991 tests.extend([RpcTest::identity(StateGetClaim::request((
992 block.miner_address,
993 *claim_id,
994 tipset.key().into(),
995 ))?)]);
996 }
997 for address in StateGetAllocations::get_valid_actor_addresses(store, tipset)?
998 .take(COLLECTION_SAMPLE_SIZE)
999 {
1000 tests.extend([RpcTest::identity(StateGetAllocations::request((
1001 address,
1002 tipset.key().into(),
1003 ))?)]);
1004 for allocation_id in StateGetAllocations::get_allocations(store, &address, tipset)?
1005 .keys()
1006 .take(COLLECTION_SAMPLE_SIZE)
1007 {
1008 tests.extend([RpcTest::identity(StateGetAllocation::request((
1009 address,
1010 *allocation_id,
1011 tipset.key().into(),
1012 ))?)]);
1013 }
1014 }
1015 for sector in StateSectorGetInfo::get_sectors(store, &block.miner_address, tipset)?
1016 .into_iter()
1017 .take(COLLECTION_SAMPLE_SIZE)
1018 {
1019 tests.extend([
1020 RpcTest::identity(StateSectorGetInfo::request((
1021 block.miner_address,
1022 sector,
1023 tipset.key().into(),
1024 ))?),
1025 RpcTest::identity(StateMinerSectors::request((
1026 block.miner_address,
1027 {
1028 let mut bf = BitField::new();
1029 bf.set(sector);
1030 Some(bf)
1031 },
1032 tipset.key().into(),
1033 ))?),
1034 RpcTest::identity(StateSectorExpiration::request((
1035 block.miner_address,
1036 sector,
1037 tipset.key().into(),
1038 ))?)
1039 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1040 RpcTest::identity(StateSectorPartition::request((
1041 block.miner_address,
1042 sector,
1043 tipset.key().into(),
1044 ))?),
1045 RpcTest::identity(StateMinerSectorAllocated::request((
1046 block.miner_address,
1047 sector,
1048 tipset.key().into(),
1049 ))?),
1050 ]);
1051 }
1052 for sector in StateSectorPreCommitInfo::get_sectors(store, &block.miner_address, tipset)?
1053 .into_iter()
1054 .take(COLLECTION_SAMPLE_SIZE)
1055 {
1056 tests.extend([RpcTest::identity(StateSectorPreCommitInfo::request((
1057 block.miner_address,
1058 sector,
1059 tipset.key().into(),
1060 ))?)]);
1061 }
1062 for info in StateSectorPreCommitInfo::get_sector_pre_commit_infos(
1063 store,
1064 &block.miner_address,
1065 tipset,
1066 )?
1067 .into_iter()
1068 .take(COLLECTION_SAMPLE_SIZE)
1069 .filter(|info| {
1070 !info.deal_ids.iter().any(|id| {
1071 if let Some(Ok(deal)) = deals_map.get(id) {
1072 tipset.epoch() > deal.start_epoch || info.expiration > deal.end_epoch
1073 } else {
1074 true
1075 }
1076 })
1077 }) {
1078 tests.extend([RpcTest::identity(
1079 StateMinerInitialPledgeCollateral::request((
1080 block.miner_address,
1081 info.clone(),
1082 tipset.key().into(),
1083 ))?,
1084 )]);
1085 tests.extend([RpcTest::identity(
1086 StateMinerPreCommitDepositForPower::request((
1087 block.miner_address,
1088 info,
1089 tipset.key().into(),
1090 ))?,
1091 )]);
1092 }
1093
1094 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
1095 for msg_cid in sample_message_cids(bls_messages.iter(), secp_messages.iter()) {
1096 tests.extend([
1097 RpcTest::identity(StateReplay::request((tipset.key().into(), msg_cid))?),
1098 validate_message_lookup(
1099 StateWaitMsg::request((msg_cid, 0, 10101, true))?
1100 .with_timeout(Duration::from_secs(15)),
1101 ),
1102 validate_message_lookup(
1103 StateWaitMsg::request((msg_cid, 0, 10101, false))?
1104 .with_timeout(Duration::from_secs(15)),
1105 ),
1106 validate_message_lookup(StateSearchMsg::request((
1107 None.into(),
1108 msg_cid,
1109 800,
1110 true,
1111 ))?),
1112 validate_message_lookup(StateSearchMsg::request((
1113 None.into(),
1114 msg_cid,
1115 800,
1116 false,
1117 ))?),
1118 validate_message_lookup(StateSearchMsgLimited::request((msg_cid, 800))?),
1119 ]);
1120 }
1121 for msg in sample_messages(bls_messages.iter(), secp_messages.iter()) {
1122 tests.extend([
1123 RpcTest::identity(StateAccountKey::request((msg.from(), tipset.key().into()))?),
1124 RpcTest::identity(StateAccountKey::request((msg.from(), Default::default()))?),
1125 RpcTest::identity(StateLookupID::request((msg.from(), tipset.key().into()))?),
1126 RpcTest::identity(StateListMessages::request((
1127 MessageFilter {
1128 from: Some(msg.from()),
1129 to: Some(msg.to()),
1130 },
1131 tipset.key().into(),
1132 tipset.epoch(),
1133 ))?),
1134 RpcTest::identity(StateListMessages::request((
1135 MessageFilter {
1136 from: Some(msg.from()),
1137 to: None,
1138 },
1139 tipset.key().into(),
1140 tipset.epoch(),
1141 ))?),
1142 RpcTest::identity(StateListMessages::request((
1143 MessageFilter {
1144 from: None,
1145 to: Some(msg.to()),
1146 },
1147 tipset.key().into(),
1148 tipset.epoch(),
1149 ))?),
1150 RpcTest::identity(StateCall::request((msg.clone(), tipset.key().into()))?),
1151 ]);
1152 }
1153 }
1154
1155 Ok(tests)
1156}
1157
1158fn wallet_tests(worker_address: Option<Address>) -> Vec<RpcTest> {
1159 let prefunded_wallets = [
1160 Address::from_str("t0168923").unwrap(), Address::from_str("t1w2zb5a723izlm4q3khclsjcnapfzxcfhvqyfoly").unwrap(),
1163 Address::from_str("t2nfplhzpyeck5dcc4fokj5ar6nbs3mhbdmq6xu3q").unwrap(),
1164 Address::from_str("t3wmbvnabsj6x2uki33phgtqqemmunnttowpx3chklrchy76pv52g5ajnaqdypxoomq5ubfk65twl5ofvkhshq").unwrap(),
1165 Address::from_str("t410fx2cumi6pgaz64varl77xbuub54bgs3k5xsvn3ki").unwrap(),
1166 *KNOWN_EMPTY_CALIBNET_ADDRESS,
1168 ];
1169
1170 let mut tests = vec![];
1171 for wallet in prefunded_wallets {
1172 tests.push(RpcTest::identity(
1173 WalletBalance::request((wallet,)).unwrap(),
1174 ));
1175 tests.push(RpcTest::identity(
1176 WalletValidateAddress::request((wallet.to_string(),)).unwrap(),
1177 ));
1178 }
1179
1180 let known_wallet = *KNOWN_CALIBNET_ADDRESS;
1181 let signature = "44364ca78d85e53dda5ac6f719a4f2de3261c17f58558ab7730f80c478e6d43775244e7d6855afad82e4a1fd6449490acfa88e3fcfe7c1fe96ed549c100900b400";
1183 let text = "Hello world!".as_bytes().to_vec();
1184 let sig_bytes = hex::decode(signature).unwrap();
1185 let signature = match known_wallet.protocol() {
1186 Protocol::Secp256k1 => Signature::new_secp256k1(sig_bytes),
1187 Protocol::BLS => Signature::new_bls(sig_bytes),
1188 _ => panic!("Invalid signature (must be bls or secp256k1)"),
1189 };
1190
1191 tests.push(RpcTest::identity(
1192 WalletBalance::request((known_wallet,)).unwrap(),
1193 ));
1194 tests.push(RpcTest::identity(
1195 WalletValidateAddress::request((known_wallet.to_string(),)).unwrap(),
1196 ));
1197 tests.push(
1198 RpcTest::identity(
1199 WalletValidateAddress::request((
1201 "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn".to_string(),
1202 ))
1203 .unwrap(),
1204 )
1205 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalErrorCaseInsensitive),
1207 );
1208 tests.push(RpcTest::identity(
1209 WalletVerify::request((known_wallet, text, signature)).unwrap(),
1210 ));
1211
1212 if let Some(worker_address) = worker_address {
1215 use base64::{Engine, prelude::BASE64_STANDARD};
1216 let msg =
1217 BASE64_STANDARD.encode("Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn".as_bytes());
1218 tests.push(RpcTest::identity(
1219 WalletSign::request((worker_address, msg.into())).unwrap(),
1220 ));
1221 tests.push(RpcTest::identity(
1222 WalletSign::request((worker_address, Vec::new())).unwrap(),
1223 ));
1224 let msg: Message = Message {
1225 from: worker_address,
1226 to: worker_address,
1227 value: TokenAmount::from_whole(1),
1228 method_num: METHOD_SEND,
1229 ..Default::default()
1230 };
1231 tests.push(RpcTest::identity(
1232 WalletSignMessage::request((worker_address, msg)).unwrap(),
1233 ));
1234 }
1235 tests
1236}
1237
1238fn eth_tests() -> Vec<RpcTest> {
1239 let mut tests = vec![];
1240 for use_alias in [false, true] {
1241 tests.push(RpcTest::identity(
1242 EthAccounts::request_with_alias((), use_alias).unwrap(),
1243 ));
1244 tests.push(RpcTest::basic(
1245 EthBlockNumber::request_with_alias((), use_alias).unwrap(),
1246 ));
1247 tests.push(RpcTest::identity(
1248 EthChainId::request_with_alias((), use_alias).unwrap(),
1249 ));
1250 tests.push(RpcTest::validate(
1252 EthGasPrice::request_with_alias((), use_alias).unwrap(),
1253 |forest, lotus| forest.0.is_positive() && lotus.0.is_positive(),
1254 ));
1255 tests.push(RpcTest::basic(
1256 EthSyncing::request_with_alias((), use_alias).unwrap(),
1257 ));
1258 tests.push(RpcTest::identity(
1259 EthGetBalance::request_with_alias(
1260 (
1261 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1262 BlockNumberOrHash::from_predefined(Predefined::Latest),
1263 ),
1264 use_alias,
1265 )
1266 .unwrap(),
1267 ));
1268 tests.push(RpcTest::identity(
1269 EthGetBalance::request_with_alias(
1270 (
1271 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1272 BlockNumberOrHash::from_predefined(Predefined::Pending),
1273 ),
1274 use_alias,
1275 )
1276 .unwrap(),
1277 ));
1278 tests.push(RpcTest::basic(
1279 Web3ClientVersion::request_with_alias((), use_alias).unwrap(),
1280 ));
1281 tests.push(RpcTest::basic(
1282 EthMaxPriorityFeePerGas::request_with_alias((), use_alias).unwrap(),
1283 ));
1284 tests.push(RpcTest::identity(
1285 EthProtocolVersion::request_with_alias((), use_alias).unwrap(),
1286 ));
1287
1288 let cases = [
1289 (
1290 Some(EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap()),
1291 Some(
1292 "0xf8b2cb4f000000000000000000000000CbfF24DED1CE6B53712078759233Ac8f91ea71B6"
1293 .parse()
1294 .unwrap(),
1295 ),
1296 ),
1297 (Some(EthAddress::from_str(ZERO_ADDRESS).unwrap()), None),
1298 (
1301 None,
1302 Some(
1303 EthBytes::from_str(
1304 concat!("0x", include_str!("contracts/cthulhu/invoke.hex")).trim(),
1305 )
1306 .unwrap(),
1307 ),
1308 ),
1309 ];
1310
1311 for (to, data) in cases {
1312 tests.push(RpcTest::identity(
1313 EthCall::request_with_alias(
1314 (
1315 EthCallMessage {
1316 to,
1317 data,
1318 ..EthCallMessage::default()
1319 },
1320 BlockNumberOrHash::from_predefined(Predefined::Latest),
1321 ),
1322 use_alias,
1323 )
1324 .unwrap(),
1325 ));
1326 }
1327
1328 let cases = [
1329 Some(EthAddressList::List(vec![])),
1330 Some(EthAddressList::List(vec![
1331 EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(),
1332 EthAddress::from_str("0x89beb26addec4bc7e9f475aacfd084300d6de719").unwrap(),
1333 ])),
1334 Some(EthAddressList::Single(
1335 EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(),
1336 )),
1337 None,
1338 ];
1339
1340 for address in cases {
1341 tests.push(RpcTest::basic(
1342 EthNewFilter::request_with_alias(
1343 (EthFilterSpec {
1344 address,
1345 ..Default::default()
1346 },),
1347 use_alias,
1348 )
1349 .unwrap(),
1350 ));
1351 }
1352 tests.push(RpcTest::basic(
1353 EthNewPendingTransactionFilter::request_with_alias((), use_alias).unwrap(),
1354 ));
1355 tests.push(RpcTest::basic(
1356 EthNewBlockFilter::request_with_alias((), use_alias).unwrap(),
1357 ));
1358 tests.push(RpcTest::identity(
1359 EthUninstallFilter::request_with_alias((FilterID::new().unwrap(),), use_alias).unwrap(),
1360 ));
1361 tests.push(RpcTest::identity(
1362 EthAddressToFilecoinAddress::request((EthAddress::from_str(
1363 "0xff38c072f286e3b20b3954ca9f99c05fbecc64aa",
1364 )
1365 .unwrap(),))
1366 .unwrap(),
1367 ));
1368 }
1369 tests
1370}
1371
1372fn eth_call_api_err_tests(epoch: i64) -> Vec<RpcTest> {
1373 let contract_codes = [
1374 include_str!("./contracts/arithmetic_err/arithmetic_overflow_err.hex"),
1375 include_str!("contracts/assert_err/assert_err.hex"),
1376 include_str!("./contracts/divide_by_zero_err/divide_by_zero_err.hex"),
1377 include_str!("./contracts/generic_panic_err/generic_panic_err.hex"),
1378 include_str!("./contracts/index_out_of_bounds_err/index_out_of_bounds_err.hex"),
1379 include_str!("./contracts/invalid_enum_err/invalid_enum_err.hex"),
1380 include_str!("./contracts/invalid_storage_array_err/invalid_storage_array_err.hex"),
1381 include_str!("./contracts/out_of_memory_err/out_of_memory_err.hex"),
1382 include_str!("./contracts/pop_empty_array_err/pop_empty_array_err.hex"),
1383 include_str!("./contracts/uninitialized_fn_err/uninitialized_fn_err.hex"),
1384 ];
1385
1386 contract_codes
1387 .iter()
1388 .map(|&contract_hex| {
1389 let contract_code =
1390 EthBytes::from_str(contract_hex).expect("Contract bytecode should be valid hex");
1391
1392 let zero_address = EthAddress::from_str(ZERO_ADDRESS).unwrap();
1393 let eth_call_request = EthCall::request((
1395 EthCallMessage {
1396 from: Some(zero_address),
1397 data: Some(contract_code),
1398 ..EthCallMessage::default()
1399 },
1400 BlockNumberOrHash::from_block_number(epoch),
1401 ))
1402 .unwrap();
1403
1404 RpcTest::identity(eth_call_request)
1405 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError)
1406 })
1407 .collect()
1408}
1409
1410fn eth_tests_with_tipset<DB: Blockstore>(store: &Arc<DB>, shared_tipset: &Tipset) -> Vec<RpcTest> {
1411 let block_cid = shared_tipset.key().cid().unwrap();
1412 let block_hash: EthHash = block_cid.into();
1413
1414 let mut tests = vec![
1415 RpcTest::identity(
1416 EthGetBalance::request((
1417 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1418 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1419 ))
1420 .unwrap(),
1421 ),
1422 RpcTest::identity(
1423 EthGetBalance::request((
1424 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1425 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1426 ))
1427 .unwrap(),
1428 ),
1429 RpcTest::identity(
1430 EthGetBalance::request((
1431 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1432 BlockNumberOrHash::from_block_number_object(shared_tipset.epoch()),
1433 ))
1434 .unwrap(),
1435 ),
1436 RpcTest::identity(
1437 EthGetBalance::request((
1438 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1439 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), false),
1440 ))
1441 .unwrap(),
1442 ),
1443 RpcTest::identity(
1444 EthGetBalance::request((
1445 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1446 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1447 ))
1448 .unwrap(),
1449 ),
1450 RpcTest::identity(
1451 EthGetBalance::request((
1452 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1453 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1454 ))
1455 .unwrap(),
1456 )
1457 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1458 RpcTest::basic(
1459 EthGetBalance::request((
1460 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1461 BlockNumberOrHash::from_predefined(Predefined::Pending),
1462 ))
1463 .unwrap(),
1464 ),
1465 RpcTest::basic(
1466 EthGetBalance::request((
1467 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1468 BlockNumberOrHash::from_predefined(Predefined::Latest),
1469 ))
1470 .unwrap(),
1471 ),
1472 RpcTest::identity(
1473 EthGetBlockByNumber::request((
1474 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1475 false,
1476 ))
1477 .unwrap(),
1478 ),
1479 RpcTest::identity(
1480 EthGetBlockByNumber::request((
1481 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1482 true,
1483 ))
1484 .unwrap(),
1485 ),
1486 RpcTest::identity(
1487 EthGetBlockByNumber::request((
1488 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest),
1489 true,
1490 ))
1491 .unwrap(),
1492 )
1493 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1494 RpcTest::basic(
1495 EthGetBlockByNumber::request((
1496 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending),
1497 true,
1498 ))
1499 .unwrap(),
1500 ),
1501 RpcTest::basic(
1502 EthGetBlockByNumber::request((
1503 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
1504 true,
1505 ))
1506 .unwrap(),
1507 ),
1508 RpcTest::basic(
1509 EthGetBlockByNumber::request((
1510 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
1511 true,
1512 ))
1513 .unwrap(),
1514 ),
1515 RpcTest::basic(
1516 EthGetBlockByNumber::request((
1517 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
1518 true,
1519 ))
1520 .unwrap(),
1521 ),
1522 RpcTest::identity(
1523 EthGetBlockReceipts::request((BlockNumberOrHash::from_block_hash_object(
1524 block_hash.clone(),
1525 true,
1526 ),))
1527 .unwrap(),
1528 ),
1529 RpcTest::basic(
1533 EthGetBlockReceipts::request((BlockNumberOrHash::from_predefined(Predefined::Latest),))
1534 .unwrap(),
1535 ),
1536 RpcTest::identity(
1537 EthGetBlockTransactionCountByHash::request((block_hash.clone(),)).unwrap(),
1538 ),
1539 RpcTest::identity(
1540 EthGetBlockReceiptsLimited::request((
1541 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1542 4,
1543 ))
1544 .unwrap(),
1545 )
1546 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1547 RpcTest::identity(
1548 EthGetBlockReceiptsLimited::request((
1549 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1550 -1,
1551 ))
1552 .unwrap(),
1553 ),
1554 RpcTest::identity(
1555 EthGetBlockTransactionCountByNumber::request((EthInt64(shared_tipset.epoch()),))
1556 .unwrap(),
1557 ),
1558 RpcTest::identity(
1559 EthGetTransactionCount::request((
1560 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1561 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1562 ))
1563 .unwrap(),
1564 ),
1565 RpcTest::identity(
1566 EthGetTransactionCount::request((
1567 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1568 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1569 ))
1570 .unwrap(),
1571 )
1572 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1573 RpcTest::basic(
1574 EthGetTransactionCount::request((
1575 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1576 BlockNumberOrHash::from_predefined(Predefined::Pending),
1577 ))
1578 .unwrap(),
1579 ),
1580 RpcTest::basic(
1581 EthGetTransactionCount::request((
1582 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1583 BlockNumberOrHash::from_predefined(Predefined::Latest),
1584 ))
1585 .unwrap(),
1586 ),
1587 RpcTest::identity(
1588 EthGetStorageAt::request((
1589 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1591 EthBytes(vec![0xa]),
1592 BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())),
1593 ))
1594 .unwrap(),
1595 ),
1596 RpcTest::identity(
1597 EthGetStorageAt::request((
1598 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1599 EthBytes(vec![0xa]),
1600 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1601 ))
1602 .unwrap(),
1603 )
1604 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1605 RpcTest::basic(
1606 EthGetStorageAt::request((
1607 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1608 EthBytes(vec![0xa]),
1609 BlockNumberOrHash::from_predefined(Predefined::Pending),
1610 ))
1611 .unwrap(),
1612 ),
1613 RpcTest::basic(
1614 EthGetStorageAt::request((
1615 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1616 EthBytes(vec![0xa]),
1617 BlockNumberOrHash::from_predefined(Predefined::Latest),
1618 ))
1619 .unwrap(),
1620 ),
1621 RpcTest::identity(
1622 EthFeeHistory::request((
1623 10.into(),
1624 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
1625 None,
1626 ))
1627 .unwrap(),
1628 ),
1629 RpcTest::identity(
1630 EthFeeHistory::request((
1631 10.into(),
1632 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
1633 Some(vec![10., 50., 90.]),
1634 ))
1635 .unwrap(),
1636 ),
1637 RpcTest::identity(
1638 EthFeeHistory::request((
1639 10.into(),
1640 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
1641 None,
1642 ))
1643 .unwrap(),
1644 )
1645 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1646 RpcTest::basic(
1647 EthFeeHistory::request((
1648 10.into(),
1649 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending),
1650 Some(vec![10., 50., 90.]),
1651 ))
1652 .unwrap(),
1653 ),
1654 RpcTest::basic(
1655 EthFeeHistory::request((
1656 10.into(),
1657 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest),
1658 None,
1659 ))
1660 .unwrap(),
1661 ),
1662 RpcTest::basic(
1663 EthFeeHistory::request((
1664 10.into(),
1665 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
1666 None,
1667 ))
1668 .unwrap(),
1669 ),
1670 RpcTest::basic(
1671 EthFeeHistory::request((
1672 10.into(),
1673 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
1674 Some(vec![10., 50., 90.]),
1675 ))
1676 .unwrap(),
1677 ),
1678 RpcTest::identity(
1679 EthGetCode::request((
1680 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1682 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1683 ))
1684 .unwrap(),
1685 ),
1686 RpcTest::identity(
1687 EthGetCode::request((
1688 Address::from_str("f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq")
1690 .unwrap()
1691 .try_into()
1692 .unwrap(),
1693 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1694 ))
1695 .unwrap(),
1696 ),
1697 RpcTest::identity(
1698 EthGetCode::request((
1699 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1700 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1701 ))
1702 .unwrap(),
1703 )
1704 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1705 RpcTest::basic(
1706 EthGetCode::request((
1707 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1708 BlockNumberOrHash::from_predefined(Predefined::Pending),
1709 ))
1710 .unwrap(),
1711 ),
1712 RpcTest::basic(
1713 EthGetCode::request((
1714 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1715 BlockNumberOrHash::from_predefined(Predefined::Latest),
1716 ))
1717 .unwrap(),
1718 ),
1719 RpcTest::identity(
1720 EthGetTransactionByBlockNumberAndIndex::request((
1721 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
1722 0.into(),
1723 ))
1724 .unwrap(),
1725 )
1726 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1727 RpcTest::identity(
1728 EthGetTransactionByBlockNumberAndIndex::request((
1729 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
1730 0.into(),
1731 ))
1732 .unwrap(),
1733 )
1734 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1735 RpcTest::identity(
1736 EthGetTransactionByBlockHashAndIndex::request((block_hash.clone(), 0.into())).unwrap(),
1737 )
1738 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1739 RpcTest::identity(EthGetBlockByHash::request((block_hash.clone(), false)).unwrap()),
1740 RpcTest::identity(EthGetBlockByHash::request((block_hash.clone(), true)).unwrap()),
1741 RpcTest::identity(
1742 EthGetLogs::request((EthFilterSpec {
1743 from_block: Some(format!("0x{:x}", shared_tipset.epoch())),
1744 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
1745 ..Default::default()
1746 },))
1747 .unwrap(),
1748 )
1749 .sort_policy(SortPolicy::All)
1750 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1751 RpcTest::identity(
1752 EthGetLogs::request((EthFilterSpec {
1753 from_block: Some(format!("0x{:x}", shared_tipset.epoch())),
1754 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
1755 address: Some(EthAddressList::List(Vec::new())),
1756 ..Default::default()
1757 },))
1758 .unwrap(),
1759 )
1760 .sort_policy(SortPolicy::All)
1761 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1762 RpcTest::identity(
1763 EthGetLogs::request((EthFilterSpec {
1764 from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)),
1765 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
1766 ..Default::default()
1767 },))
1768 .unwrap(),
1769 )
1770 .sort_policy(SortPolicy::All)
1771 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1772 RpcTest::identity(
1773 EthGetLogs::request((EthFilterSpec {
1774 address: Some(EthAddressList::Single(
1775 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
1776 )),
1777 ..Default::default()
1778 },))
1779 .unwrap(),
1780 )
1781 .sort_policy(SortPolicy::All)
1782 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1783 RpcTest::identity(EthGetFilterLogs::request((FilterID::new().unwrap(),)).unwrap())
1784 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1785 RpcTest::identity(EthGetFilterChanges::request((FilterID::new().unwrap(),)).unwrap())
1786 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1787 RpcTest::identity(EthGetTransactionHashByCid::request((block_cid,)).unwrap()),
1788 RpcTest::identity(
1789 EthTraceBlock::request((ExtBlockNumberOrHash::from_block_number(
1790 shared_tipset.epoch(),
1791 ),))
1792 .unwrap(),
1793 ),
1794 RpcTest::identity(
1795 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
1796 ExtPredefined::Earliest,
1797 ),))
1798 .unwrap(),
1799 )
1800 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1801 RpcTest::basic(
1802 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
1803 ExtPredefined::Pending,
1804 ),))
1805 .unwrap(),
1806 ),
1807 RpcTest::basic(
1808 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),))
1809 .unwrap(),
1810 ),
1811 RpcTest::basic(
1812 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),))
1813 .unwrap(),
1814 ),
1815 RpcTest::basic(
1816 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
1817 ExtPredefined::Finalized,
1818 ),))
1819 .unwrap(),
1820 ),
1821 RpcTest::identity(
1822 EthTraceReplayBlockTransactions::request((
1823 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1824 vec!["trace".to_string()],
1825 ))
1826 .unwrap(),
1827 ),
1828 RpcTest::identity(
1829 EthTraceFilter::request((EthTraceFilterCriteria {
1830 from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)),
1831 to_block: Some(format!("0x{:x}", shared_tipset.epoch() - SAFE_EPOCH_DELAY)),
1832 ..Default::default()
1833 },))
1834 .unwrap(),
1835 )
1836 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1839 ];
1840
1841 for block in shared_tipset.block_headers() {
1842 let (bls_messages, secp_messages) =
1843 crate::chain::store::block_messages(store, block).unwrap();
1844 for msg in sample_messages(bls_messages.iter(), secp_messages.iter()) {
1845 if let Ok(eth_to_addr) = msg.to.try_into() {
1846 tests.extend([RpcTest::identity(
1847 EthEstimateGas::request((
1848 EthCallMessage {
1849 to: Some(eth_to_addr),
1850 value: Some(msg.value.clone().into()),
1851 data: Some(msg.params.clone().into()),
1852 ..Default::default()
1853 },
1854 Some(BlockNumberOrHash::BlockNumber(shared_tipset.epoch().into())),
1855 ))
1856 .unwrap(),
1857 )
1858 .policy_on_rejected(PolicyOnRejected::Pass)]);
1859 }
1860 }
1861 }
1862
1863 tests
1864}
1865
1866fn read_state_api_tests(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
1867 let tests = vec![
1868 RpcTest::identity(StateReadState::request((
1869 Address::SYSTEM_ACTOR,
1870 tipset.key().into(),
1871 ))?),
1872 RpcTest::identity(StateReadState::request((
1873 Address::SYSTEM_ACTOR,
1874 Default::default(),
1875 ))?),
1876 RpcTest::identity(StateReadState::request((
1877 Address::CRON_ACTOR,
1878 tipset.key().into(),
1879 ))?),
1880 RpcTest::identity(StateReadState::request((
1881 Address::MARKET_ACTOR,
1882 tipset.key().into(),
1883 ))?),
1884 RpcTest::identity(StateReadState::request((
1885 Address::INIT_ACTOR,
1886 tipset.key().into(),
1887 ))?),
1888 RpcTest::identity(StateReadState::request((
1889 Address::POWER_ACTOR,
1890 tipset.key().into(),
1891 ))?),
1892 RpcTest::identity(StateReadState::request((
1893 Address::REWARD_ACTOR,
1894 tipset.key().into(),
1895 ))?),
1896 RpcTest::identity(StateReadState::request((
1897 Address::VERIFIED_REGISTRY_ACTOR,
1898 tipset.key().into(),
1899 ))?),
1900 RpcTest::identity(StateReadState::request((
1901 Address::DATACAP_TOKEN_ACTOR,
1902 tipset.key().into(),
1903 ))?),
1904 RpcTest::identity(StateReadState::request((
1905 Address::new_id(66116), tipset.key().into(),
1908 ))?),
1909 RpcTest::identity(StateReadState::request((
1910 Address::new_id(18101), tipset.key().into(),
1913 ))?),
1914 RpcTest::identity(StateReadState::request((
1915 ACCOUNT_ADDRESS,
1916 tipset.key().into(),
1917 ))?),
1918 RpcTest::identity(StateReadState::request((
1919 MINER_ADDRESS,
1920 tipset.key().into(),
1921 ))?),
1922 RpcTest::identity(StateReadState::request((
1923 Address::from_str(EVM_ADDRESS).unwrap(), tipset.key().into(),
1925 ))?),
1926 ];
1927
1928 Ok(tests)
1929}
1930
1931fn eth_state_tests_with_tipset<DB: Blockstore>(
1932 store: &Arc<DB>,
1933 shared_tipset: &Tipset,
1934 eth_chain_id: EthChainIdType,
1935) -> anyhow::Result<Vec<RpcTest>> {
1936 let mut tests = vec![];
1937
1938 for block in shared_tipset.block_headers() {
1939 let state = StateTree::new_from_root(store.clone(), shared_tipset.parent_state())?;
1940
1941 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
1942 for smsg in sample_signed_messages(bls_messages.iter(), secp_messages.iter()) {
1943 let tx = new_eth_tx_from_signed_message(&smsg, &state, eth_chain_id)?;
1944 tests.push(RpcTest::identity(
1945 EthGetMessageCidByTransactionHash::request((tx.hash.clone(),))?,
1946 ));
1947 tests.push(RpcTest::identity(EthGetTransactionByHash::request((tx
1948 .hash
1949 .clone(),))?));
1950 tests.push(RpcTest::identity(EthGetTransactionByHashLimited::request(
1951 (tx.hash.clone(), shared_tipset.epoch()),
1952 )?));
1953 tests.push(RpcTest::identity(
1954 EthTraceTransaction::request((tx.hash.to_string(),)).unwrap(),
1955 ));
1956 if smsg.message.from.protocol() == Protocol::Delegated
1957 && smsg.message.to.protocol() == Protocol::Delegated
1958 {
1959 tests.push(
1960 RpcTest::identity(EthGetTransactionReceipt::request((tx.hash.clone(),))?)
1961 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1962 );
1963 tests.push(
1964 RpcTest::identity(EthGetTransactionReceiptLimited::request((tx.hash, 800))?)
1965 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1966 );
1967 }
1968 }
1969 }
1970 tests.push(RpcTest::identity(
1971 EthGetMessageCidByTransactionHash::request((EthHash::from_str(
1972 "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355f",
1973 )?,))?,
1974 ));
1975
1976 tests.extend(eth_call_api_err_tests(shared_tipset.epoch()));
1978
1979 Ok(tests)
1980}
1981
1982fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec<RpcTest> {
1983 let addr = Address::from_str("t15ydyu3d65gznpp2qxwpkjsgz4waubeunn6upvla").unwrap();
1986 let message = Message {
1987 from: addr,
1988 to: addr,
1989 value: TokenAmount::from_whole(1),
1990 method_num: METHOD_SEND,
1991 ..Default::default()
1992 };
1993
1994 vec![RpcTest::identity(
2000 GasEstimateGasLimit::request((message, shared_tipset.key().into())).unwrap(),
2001 )]
2002}
2003
2004fn f3_tests() -> anyhow::Result<Vec<RpcTest>> {
2005 Ok(vec![
2006 RpcTest::basic(F3GetECPowerTable::request((None.into(),))?),
2008 RpcTest::basic(F3GetLatestCertificate::request(())?),
2009 RpcTest::basic(F3ListParticipants::request(())?),
2010 RpcTest::basic(F3GetProgress::request(())?),
2011 RpcTest::basic(F3GetOrRenewParticipationTicket::request((
2012 Address::new_id(1000),
2013 vec![],
2014 3,
2015 ))?),
2016 RpcTest::identity(F3IsRunning::request(())?),
2017 RpcTest::identity(F3GetCertificate::request((0,))?),
2018 RpcTest::identity(F3GetCertificate::request((100,))?),
2019 RpcTest::identity(F3GetManifest::request(())?),
2020 ])
2021}
2022
2023fn f3_tests_with_tipset(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
2024 Ok(vec![
2025 RpcTest::identity(F3GetECPowerTable::request((tipset.key().into(),))?),
2026 RpcTest::identity(F3GetF3PowerTable::request((tipset.key().into(),))?),
2027 ])
2028}
2029
2030fn snapshot_tests(
2033 store: Arc<ManyCar>,
2034 num_tipsets: usize,
2035 miner_address: Option<Address>,
2036 eth_chain_id: u64,
2037) -> anyhow::Result<Vec<RpcTest>> {
2038 let mut tests = vec![];
2039 let shared_tipset = store
2042 .heaviest_tipset()?
2043 .chain(&store)
2044 .take(SAFE_EPOCH_DELAY as usize)
2045 .last()
2046 .expect("Infallible");
2047
2048 for tipset in shared_tipset.chain(&store).take(num_tipsets) {
2049 tests.extend(chain_tests_with_tipset(&store, &tipset)?);
2050 tests.extend(miner_tests_with_tipset(&store, &tipset, miner_address)?);
2051 tests.extend(state_tests_with_tipset(&store, &tipset)?);
2052 tests.extend(eth_tests_with_tipset(&store, &tipset));
2053 tests.extend(event_tests_with_tipset(&store, &tipset));
2054 tests.extend(gas_tests_with_tipset(&tipset));
2055 tests.extend(mpool_tests_with_tipset(&tipset));
2056 tests.extend(eth_state_tests_with_tipset(&store, &tipset, eth_chain_id)?);
2057 tests.extend(f3_tests_with_tipset(&tipset)?);
2058 }
2059
2060 Ok(tests)
2061}
2062
2063fn sample_message_cids<'a>(
2064 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2065 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2066) -> impl Iterator<Item = Cid> + 'a {
2067 bls_messages
2068 .map(|m| m.cid())
2069 .unique()
2070 .take(COLLECTION_SAMPLE_SIZE)
2071 .chain(
2072 secp_messages
2073 .map(|m| m.cid())
2074 .unique()
2075 .take(COLLECTION_SAMPLE_SIZE),
2076 )
2077 .unique()
2078}
2079
2080fn sample_messages<'a>(
2081 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2082 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2083) -> impl Iterator<Item = &'a Message> + 'a {
2084 bls_messages
2085 .unique()
2086 .take(COLLECTION_SAMPLE_SIZE)
2087 .chain(
2088 secp_messages
2089 .map(SignedMessage::message)
2090 .unique()
2091 .take(COLLECTION_SAMPLE_SIZE),
2092 )
2093 .unique()
2094}
2095
2096fn sample_signed_messages<'a>(
2097 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2098 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2099) -> impl Iterator<Item = SignedMessage> + 'a {
2100 bls_messages
2101 .unique()
2102 .take(COLLECTION_SAMPLE_SIZE)
2103 .map(|msg| {
2104 let sig = Signature::new_bls(vec![]);
2105 SignedMessage::new_unchecked(msg.clone(), sig)
2106 })
2107 .chain(secp_messages.cloned().unique().take(COLLECTION_SAMPLE_SIZE))
2108 .unique()
2109}
2110
2111pub(super) async fn create_tests(
2112 CreateTestsArgs {
2113 n_tipsets,
2114 miner_address,
2115 worker_address,
2116 eth_chain_id,
2117 snapshot_files,
2118 }: CreateTestsArgs,
2119) -> anyhow::Result<Vec<RpcTest>> {
2120 let mut tests = vec![];
2121 tests.extend(auth_tests()?);
2122 tests.extend(common_tests());
2123 tests.extend(chain_tests());
2124 tests.extend(mpool_tests());
2125 tests.extend(net_tests());
2126 tests.extend(node_tests());
2127 tests.extend(wallet_tests(worker_address));
2128 tests.extend(eth_tests());
2129 tests.extend(f3_tests()?);
2130 if !snapshot_files.is_empty() {
2131 let store = Arc::new(ManyCar::try_from(snapshot_files.clone())?);
2132 revalidate_chain(store.clone(), n_tipsets).await?;
2133 tests.extend(snapshot_tests(
2134 store,
2135 n_tipsets,
2136 miner_address,
2137 eth_chain_id,
2138 )?);
2139 }
2140 tests.sort_by_key(|test| test.request.method_name.clone());
2141
2142 tests.extend(create_deferred_tests(snapshot_files)?);
2143 Ok(tests)
2144}
2145
2146fn create_deferred_tests(snapshot_files: Vec<PathBuf>) -> anyhow::Result<Vec<RpcTest>> {
2148 let mut tests = vec![];
2149
2150 if !snapshot_files.is_empty() {
2151 let store = Arc::new(ManyCar::try_from(snapshot_files)?);
2152 tests.push(RpcTest::identity(ChainSetHead::request((store
2153 .heaviest_tipset()?
2154 .key()
2155 .clone(),))?));
2156 }
2157
2158 Ok(tests)
2159}
2160
2161async fn revalidate_chain(db: Arc<ManyCar>, n_ts_to_validate: usize) -> anyhow::Result<()> {
2162 if n_ts_to_validate == 0 {
2163 return Ok(());
2164 }
2165 let chain_config = Arc::new(handle_chain_config(&NetworkChain::Calibnet)?);
2166
2167 let genesis_header = crate::genesis::read_genesis_header(
2168 None,
2169 chain_config.genesis_bytes(&db).await?.as_deref(),
2170 &db,
2171 )
2172 .await?;
2173 let chain_store = Arc::new(ChainStore::new(
2174 db.clone(),
2175 db.clone(),
2176 db.clone(),
2177 db.clone(),
2178 chain_config,
2179 genesis_header.clone(),
2180 )?);
2181 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
2182 let head_ts = Arc::new(db.heaviest_tipset()?);
2183
2184 proofs_api::maybe_set_proofs_parameter_cache_dir_env(&Config::default().client.data_dir);
2187 ensure_proof_params_downloaded().await?;
2188 state_manager.validate_tipsets(
2189 head_ts
2190 .chain_arc(&db)
2191 .take(SAFE_EPOCH_DELAY as usize + n_ts_to_validate),
2192 )?;
2193
2194 Ok(())
2195}
2196
2197#[allow(clippy::too_many_arguments)]
2198pub(super) async fn run_tests(
2199 tests: impl IntoIterator<Item = RpcTest>,
2200 forest: impl Into<Arc<rpc::Client>>,
2201 lotus: impl Into<Arc<rpc::Client>>,
2202 max_concurrent_requests: usize,
2203 filter_file: Option<PathBuf>,
2204 filter: String,
2205 run_ignored: RunIgnored,
2206 fail_fast: bool,
2207 dump_dir: Option<PathBuf>,
2208 test_criteria_overrides: &[TestCriteriaOverride],
2209 report_dir: Option<PathBuf>,
2210 report_mode: ReportMode,
2211) -> anyhow::Result<()> {
2212 let forest = Into::<Arc<rpc::Client>>::into(forest);
2213 let lotus = Into::<Arc<rpc::Client>>::into(lotus);
2214 let semaphore = Arc::new(Semaphore::new(max_concurrent_requests));
2215 let mut futures = FuturesUnordered::new();
2216
2217 let filter_list = if let Some(filter_file) = &filter_file {
2218 FilterList::new_from_file(filter_file)?
2219 } else {
2220 FilterList::default().allow(filter.clone())
2221 };
2222
2223 let mut report_builder = ReportBuilder::new(&filter_list, report_mode);
2225
2226 for test in tests.into_iter().unique_by(
2228 |RpcTest {
2229 request:
2230 rpc::Request {
2231 method_name,
2232 params,
2233 api_paths,
2234 ..
2235 },
2236 ignore,
2237 ..
2238 }| {
2239 (
2240 method_name.clone(),
2241 params.clone(),
2242 *api_paths,
2243 ignore.is_some(),
2244 )
2245 },
2246 ) {
2247 if matches!(run_ignored, RunIgnored::Default) && test.ignore.is_some() {
2249 continue;
2250 }
2251 if matches!(run_ignored, RunIgnored::IgnoredOnly) && test.ignore.is_none() {
2253 continue;
2254 }
2255
2256 if !filter_list.authorize(&test.request.method_name) {
2257 continue;
2258 }
2259
2260 let permit = semaphore.clone().acquire_owned().await?;
2262 let forest = forest.clone();
2263 let lotus = lotus.clone();
2264 let future = tokio::spawn(async move {
2265 let test_result = test.run(&forest, &lotus).await;
2266 drop(permit); (test, test_result)
2268 });
2269
2270 futures.push(future);
2271 }
2272
2273 if futures.is_empty() {
2275 return Ok(());
2276 }
2277
2278 while let Some(Ok((test, test_result))) = futures.next().await {
2279 let method_name = test.request.method_name.clone();
2280 let success = evaluate_test_success(&test_result, &test, test_criteria_overrides);
2281
2282 report_builder.track_test_result(
2283 method_name.as_ref(),
2284 success,
2285 &test_result,
2286 &test.request.params,
2287 );
2288
2289 if let (Some(dump_dir), Some(test_dump)) = (&dump_dir, &test_result.test_dump) {
2291 dump_test_data(dump_dir, success, test_dump)?;
2292 }
2293
2294 if !success && fail_fast {
2295 break;
2296 }
2297 }
2298
2299 let has_failures = report_builder.has_failures();
2300 report_builder.print_summary();
2301
2302 if let Some(path) = report_dir {
2303 report_builder.finalize_and_save(&path)?;
2304 }
2305
2306 anyhow::ensure!(!has_failures, "Some tests failed");
2307
2308 Ok(())
2309}
2310
2311fn evaluate_test_success(
2313 test_result: &TestResult,
2314 test: &RpcTest,
2315 test_criteria_overrides: &[TestCriteriaOverride],
2316) -> bool {
2317 match (&test_result.forest_status, &test_result.lotus_status) {
2318 (TestSummary::Valid, TestSummary::Valid) => true,
2319 (TestSummary::Valid, TestSummary::Timeout) => {
2320 test_criteria_overrides.contains(&TestCriteriaOverride::ValidAndTimeout)
2321 }
2322 (TestSummary::Timeout, TestSummary::Timeout) => {
2323 test_criteria_overrides.contains(&TestCriteriaOverride::TimeoutAndTimeout)
2324 }
2325 (TestSummary::Rejected(reason_forest), TestSummary::Rejected(reason_lotus)) => {
2326 match test.policy_on_rejected {
2327 PolicyOnRejected::Pass => true,
2328 PolicyOnRejected::PassWithIdenticalError => reason_forest == reason_lotus,
2329 PolicyOnRejected::PassWithIdenticalErrorCaseInsensitive => {
2330 reason_forest.eq_ignore_ascii_case(reason_lotus)
2331 }
2332 PolicyOnRejected::PassWithQuasiIdenticalError => {
2333 reason_lotus.contains(reason_forest) || reason_forest.contains(reason_lotus)
2334 }
2335 _ => false,
2336 }
2337 }
2338 _ => false,
2339 }
2340}
2341
2342fn dump_test_data(dump_dir: &Path, success: bool, test_dump: &TestDump) -> anyhow::Result<()> {
2344 let dir = dump_dir.join(if success { "valid" } else { "invalid" });
2345 if !dir.is_dir() {
2346 std::fs::create_dir_all(&dir)?;
2347 }
2348 let file_name = format!(
2349 "{}_{}.json",
2350 test_dump
2351 .request
2352 .method_name
2353 .as_ref()
2354 .replace(".", "_")
2355 .to_lowercase(),
2356 Utc::now().timestamp_micros()
2357 );
2358 std::fs::write(
2359 dir.join(file_name),
2360 serde_json::to_string_pretty(test_dump)?,
2361 )?;
2362 Ok(())
2363}
2364
2365fn validate_message_lookup(req: rpc::Request<MessageLookup>) -> RpcTest {
2366 RpcTest::validate(req, |mut forest, mut lotus| {
2367 forest.return_dec = Ipld::Null;
2369 lotus.return_dec = Ipld::Null;
2370 forest == lotus
2371 })
2372}