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, GasEstimateMessageGas};
19use crate::rpc::miner::BlockTemplate;
20use crate::rpc::misc::ActorEventFilter;
21use crate::rpc::state::StateGetAllClaims;
22use crate::rpc::types::*;
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 libsecp256k1::{PublicKey, SecretKey};
56use num_traits::Signed;
57use serde::de::DeserializeOwned;
58use serde::{Deserialize, Serialize};
59use serde_json::Value;
60use similar::{ChangeTag, TextDiff};
61use std::borrow::Cow;
62use std::path::Path;
63use std::time::Instant;
64use std::{
65 path::PathBuf,
66 str::FromStr,
67 sync::{Arc, LazyLock},
68 time::Duration,
69};
70use tokio::sync::Semaphore;
71use tracing::debug;
72
73const COLLECTION_SAMPLE_SIZE: usize = 5;
74
75static KNOWN_CALIBNET_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
78 crate::shim::address::Network::Testnet
79 .parse_address("t1c4dkec3qhrnrsa4mccy7qntkyq2hhsma4sq7lui")
80 .unwrap()
81 .into()
82});
83
84static KNOWN_EMPTY_CALIBNET_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
86 crate::shim::address::Network::Testnet
87 .parse_address("t1qb2x5qctp34rxd7ucl327h5ru6aazj2heno7x5y")
88 .unwrap()
89 .into()
90});
91
92static KNOWN_CALIBNET_F0_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
94 crate::shim::address::Network::Testnet
95 .parse_address("t0168923")
96 .unwrap()
97 .into()
98});
99
100static KNOWN_CALIBNET_F1_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
101 crate::shim::address::Network::Testnet
102 .parse_address("t1w2zb5a723izlm4q3khclsjcnapfzxcfhvqyfoly")
103 .unwrap()
104 .into()
105});
106
107static KNOWN_CALIBNET_F2_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
108 crate::shim::address::Network::Testnet
109 .parse_address("t2nfplhzpyeck5dcc4fokj5ar6nbs3mhbdmq6xu3q")
110 .unwrap()
111 .into()
112});
113
114static KNOWN_CALIBNET_F3_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
115 crate::shim::address::Network::Testnet
116 .parse_address("t3wmbvnabsj6x2uki33phgtqqemmunnttowpx3chklrchy76pv52g5ajnaqdypxoomq5ubfk65twl5ofvkhshq")
117 .unwrap()
118 .into()
119});
120
121static KNOWN_CALIBNET_F4_ADDRESS: LazyLock<Address> = LazyLock::new(|| {
122 crate::shim::address::Network::Testnet
123 .parse_address("t410fx2cumi6pgaz64varl77xbuub54bgs3k5xsvn3ki")
124 .unwrap()
125 .into()
126});
127
128fn generate_eth_random_address() -> anyhow::Result<EthAddress> {
129 let rng = &mut crate::utils::rand::forest_os_rng();
130 let secret_key = SecretKey::random(rng);
131 let public_key = PublicKey::from_secret_key(&secret_key);
132 EthAddress::eth_address_from_pub_key(&public_key.serialize())
133}
134
135const TICKET_QUALITY_GREEDY: f64 = 0.9;
136const TICKET_QUALITY_OPTIMAL: f64 = 0.8;
137const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";
138const MINER_ADDRESS: Address = Address::new_id(78216); const ACCOUNT_ADDRESS: Address = Address::new_id(1234); const EVM_ADDRESS: &str = "t410fbqoynu2oi2lxam43knqt6ordiowm2ywlml27z4i";
142
143#[derive(
145 Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, strum::Display,
146)]
147#[serde(rename_all = "snake_case")]
148pub enum TestSummary {
149 MissingMethod,
151 Rejected(String),
153 NotJsonRPC,
155 InfraError,
157 BadJson,
159 CustomCheckFailed,
161 Timeout,
163 Valid,
165}
166
167impl TestSummary {
168 fn from_err(err: &rpc::ClientError) -> Self {
169 match err {
170 rpc::ClientError::Call(it) => match it.code().into() {
171 ErrorCode::MethodNotFound => Self::MissingMethod,
172 _ => {
173 let message = normalized_error_message(it.message());
176 Self::Rejected(message.to_string())
177 }
178 },
179 rpc::ClientError::ParseError(_) => Self::NotJsonRPC,
180 rpc::ClientError::RequestTimeout => Self::Timeout,
181 rpc::ClientError::Transport(_)
182 | rpc::ClientError::RestartNeeded(_)
183 | rpc::ClientError::InvalidSubscriptionId
184 | rpc::ClientError::InvalidRequestId(_)
185 | rpc::ClientError::Custom(_)
186 | rpc::ClientError::HttpNotImplemented
187 | rpc::ClientError::EmptyBatchRequest(_)
188 | rpc::ClientError::RegisterMethod(_) => Self::InfraError,
189 _ => unimplemented!(),
190 }
191 }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct TestDump {
197 pub request: rpc::Request,
198 pub path: rpc::ApiPaths,
199 pub forest_response: Result<Value, String>,
200 pub lotus_response: Result<Value, String>,
201}
202
203impl std::fmt::Display for TestDump {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 writeln!(f, "Request path: {}", self.path.path())?;
206 writeln!(f, "Request dump: {:?}", self.request)?;
207 writeln!(f, "Request params JSON: {}", self.request.params)?;
208 let (forest_response, lotus_response) = (
209 self.forest_response
210 .as_ref()
211 .ok()
212 .and_then(|v| serde_json::to_string_pretty(v).ok()),
213 self.lotus_response
214 .as_ref()
215 .ok()
216 .and_then(|v| serde_json::to_string_pretty(v).ok()),
217 );
218 if let Some(forest_response) = &forest_response
219 && let Some(lotus_response) = &lotus_response
220 {
221 let diff = TextDiff::from_lines(forest_response, lotus_response);
222 let mut print_diff = Vec::new();
223 for change in diff.iter_all_changes() {
224 let sign = match change.tag() {
225 ChangeTag::Delete => "-",
226 ChangeTag::Insert => "+",
227 ChangeTag::Equal => " ",
228 };
229 print_diff.push(format!("{sign}{change}"));
230 }
231 writeln!(f, "Forest response: {forest_response}")?;
232 writeln!(f, "Lotus response: {lotus_response}")?;
233 writeln!(f, "Diff: {}", print_diff.join("\n"))?;
234 } else {
235 if let Some(forest_response) = &forest_response {
236 writeln!(f, "Forest response: {forest_response}")?;
237 }
238 if let Some(lotus_response) = &lotus_response {
239 writeln!(f, "Lotus response: {lotus_response}")?;
240 }
241 };
242 Ok(())
243 }
244}
245
246pub struct TestResult {
248 pub forest_status: TestSummary,
250 pub lotus_status: TestSummary,
252 pub test_dump: Option<TestDump>,
254 pub duration: Duration,
256}
257
258pub(super) enum PolicyOnRejected {
259 Fail,
260 Pass,
261 PassWithIdenticalError,
262 PassWithIdenticalErrorCaseInsensitive,
263 PassWithQuasiIdenticalError,
266}
267
268pub(super) enum SortPolicy {
269 All,
271}
272
273pub(super) struct RpcTest {
274 pub request: rpc::Request,
275 pub check_syntax: Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>,
276 pub check_semantics: Arc<dyn Fn(serde_json::Value, serde_json::Value) -> bool + Send + Sync>,
277 pub ignore: Option<&'static str>,
278 pub policy_on_rejected: PolicyOnRejected,
279 pub sort_policy: Option<SortPolicy>,
280}
281
282fn sort_json(value: &mut Value) {
283 match value {
284 Value::Array(arr) => {
285 for v in arr.iter_mut() {
286 sort_json(v);
287 }
288 arr.sort_by_key(|a| a.to_string());
289 }
290 Value::Object(obj) => {
291 let mut sorted_map: serde_json::Map<String, Value> = serde_json::Map::new();
292 let mut keys: Vec<String> = obj.keys().cloned().collect();
293 keys.sort();
294 for k in keys {
295 let mut v = obj.remove(&k).unwrap();
296 sort_json(&mut v);
297 sorted_map.insert(k, v);
298 }
299 *obj = sorted_map;
300 }
301 _ => (),
302 }
303}
304
305impl RpcTest {
309 fn basic<T>(request: rpc::Request<T>) -> Self
312 where
313 T: HasLotusJson,
314 {
315 Self::basic_raw(request.map_ty::<T::LotusJson>())
316 }
317 fn basic_raw<T: DeserializeOwned>(request: rpc::Request<T>) -> Self {
319 Self {
320 request: request.map_ty(),
321 check_syntax: Arc::new(|it| match serde_json::from_value::<T>(it) {
322 Ok(_) => true,
323 Err(e) => {
324 debug!(?e);
325 false
326 }
327 }),
328 check_semantics: Arc::new(|_, _| true),
329 ignore: None,
330 policy_on_rejected: PolicyOnRejected::Fail,
331 sort_policy: None,
332 }
333 }
334 fn validate<T: HasLotusJson>(
337 request: rpc::Request<T>,
338 validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
339 ) -> Self {
340 Self::validate_raw(request.map_ty::<T::LotusJson>(), move |l, r| {
341 validate(T::from_lotus_json(l), T::from_lotus_json(r))
342 })
343 }
344 fn validate_raw<T: DeserializeOwned>(
346 request: rpc::Request<T>,
347 validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
348 ) -> Self {
349 Self {
350 request: request.map_ty(),
351 check_syntax: Arc::new(|value| match serde_json::from_value::<T>(value) {
352 Ok(_) => true,
353 Err(e) => {
354 debug!("{e}");
355 false
356 }
357 }),
358 check_semantics: Arc::new(move |forest_json, lotus_json| {
359 match (
360 serde_json::from_value::<T>(forest_json),
361 serde_json::from_value::<T>(lotus_json),
362 ) {
363 (Ok(forest), Ok(lotus)) => validate(forest, lotus),
364 (forest, lotus) => {
365 if let Err(e) = forest {
366 debug!("[forest] invalid json: {e}");
367 }
368 if let Err(e) = lotus {
369 debug!("[lotus] invalid json: {e}");
370 }
371 false
372 }
373 }
374 }),
375 ignore: None,
376 policy_on_rejected: PolicyOnRejected::Fail,
377 sort_policy: None,
378 }
379 }
380 pub(crate) fn identity<T: PartialEq + HasLotusJson>(request: rpc::Request<T>) -> RpcTest {
383 Self::validate(request, |forest, lotus| forest == lotus)
384 }
385
386 fn ignore(mut self, msg: &'static str) -> Self {
387 self.ignore = Some(msg);
388 self
389 }
390
391 fn policy_on_rejected(mut self, policy: PolicyOnRejected) -> Self {
392 self.policy_on_rejected = policy;
393 self
394 }
395
396 fn sort_policy(mut self, policy: SortPolicy) -> Self {
397 self.sort_policy = Some(policy);
398 self
399 }
400
401 async fn run(&self, forest: &rpc::Client, lotus: &rpc::Client) -> TestResult {
402 let start = Instant::now();
403 let forest_resp = forest.call(self.request.clone()).await;
404 let forest_response = forest_resp.as_ref().map_err(|e| e.to_string()).cloned();
405 let lotus_resp = lotus.call(self.request.clone()).await;
406 let lotus_response = lotus_resp.as_ref().map_err(|e| e.to_string()).cloned();
407
408 let (forest_status, lotus_status) = match (forest_resp, lotus_resp) {
409 (Ok(forest), Ok(lotus))
410 if (self.check_syntax)(forest.clone()) && (self.check_syntax)(lotus.clone()) =>
411 {
412 let (forest, lotus) = if self.sort_policy.is_some() {
413 let mut sorted_forest = forest.clone();
414 sort_json(&mut sorted_forest);
415 let mut sorted_lotus = lotus.clone();
416 sort_json(&mut sorted_lotus);
417 (sorted_forest, sorted_lotus)
418 } else {
419 (forest, lotus)
420 };
421 let forest_status = if (self.check_semantics)(forest, lotus) {
422 TestSummary::Valid
423 } else {
424 TestSummary::CustomCheckFailed
425 };
426 (forest_status, TestSummary::Valid)
427 }
428 (forest_resp, lotus_resp) => {
429 let forest_status = forest_resp.map_or_else(
430 |e| TestSummary::from_err(&e),
431 |value| {
432 if (self.check_syntax)(value) {
433 TestSummary::Valid
434 } else {
435 TestSummary::BadJson
436 }
437 },
438 );
439 let lotus_status = lotus_resp.map_or_else(
440 |e| TestSummary::from_err(&e),
441 |value| {
442 if (self.check_syntax)(value) {
443 TestSummary::Valid
444 } else {
445 TestSummary::BadJson
446 }
447 },
448 );
449
450 (forest_status, lotus_status)
451 }
452 };
453
454 TestResult {
455 forest_status,
456 lotus_status,
457 test_dump: Some(TestDump {
458 request: self.request.clone(),
459 path: self.request.api_path().expect("invalid api paths"),
460 forest_response,
461 lotus_response,
462 }),
463 duration: start.elapsed(),
464 }
465 }
466}
467
468fn common_tests() -> Vec<RpcTest> {
469 vec![
470 RpcTest::validate(Version::request(()).unwrap(), |forest, lotus| {
472 forest.api_version == lotus.api_version && forest.block_delay == lotus.block_delay
473 }),
474 RpcTest::basic(StartTime::request(()).unwrap()),
475 RpcTest::basic(Session::request(()).unwrap()),
476 ]
477}
478
479fn chain_tests() -> Vec<RpcTest> {
480 vec![
481 RpcTest::basic(ChainHead::request(()).unwrap()),
482 RpcTest::identity(ChainGetGenesis::request(()).unwrap()),
483 ]
484}
485
486fn chain_tests_with_tipset<DB: Blockstore>(
487 store: &Arc<DB>,
488 offline: bool,
489 tipset: &Tipset,
490) -> anyhow::Result<Vec<RpcTest>> {
491 let mut tests = vec![
492 RpcTest::identity(ChainGetTipSetByHeight::request((
493 tipset.epoch(),
494 Default::default(),
495 ))?),
496 RpcTest::identity(ChainGetTipSetAfterHeight::request((
497 tipset.epoch(),
498 Default::default(),
499 ))?),
500 RpcTest::identity(ChainGetTipSet::request((tipset.key().into(),))?),
501 RpcTest::identity(ChainGetTipSet::request((None.into(),))?)
502 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
503 RpcTest::identity(ChainGetTipSetV2::request((TipsetSelector {
504 key: None.into(),
505 height: None,
506 tag: None,
507 },))?)
508 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
509 RpcTest::identity(ChainGetTipSetV2::request((TipsetSelector {
510 key: tipset.key().into(),
511 height: None,
512 tag: Some(TipsetTag::Latest),
513 },))?)
514 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
515 RpcTest::identity(ChainGetTipSetV2::request((TipsetSelector {
516 key: tipset.key().into(),
517 height: None,
518 tag: None,
519 },))?),
520 RpcTest::identity(ChainGetTipSetV2::request((TipsetSelector {
521 key: None.into(),
522 height: Some(TipsetHeight {
523 at: tipset.epoch(),
524 previous: true,
525 anchor: Some(TipsetAnchor {
526 key: None.into(),
527 tag: None,
528 }),
529 }),
530 tag: None,
531 },))?),
532 RpcTest::identity(ChainGetTipSetV2::request((TipsetSelector {
533 key: None.into(),
534 height: Some(TipsetHeight {
535 at: tipset.epoch(),
536 previous: true,
537 anchor: None,
538 }),
539 tag: None,
540 },))?)
541 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
542 .ignore("this case should pass when F3 is back on calibnet"),
543 validate_tagged_tipset_v2(
544 ChainGetTipSetV2::request((TipsetSelector {
545 key: None.into(),
546 height: None,
547 tag: Some(TipsetTag::Latest),
548 },))?,
549 offline,
550 ),
551 RpcTest::identity(ChainGetPath::request((
552 tipset.key().clone(),
553 tipset.parents().clone(),
554 ))?),
555 RpcTest::identity(ChainGetMessagesInTipset::request((tipset
556 .key()
557 .clone()
558 .into(),))?),
559 RpcTest::identity(ChainTipSetWeight::request((tipset.key().into(),))?),
560 RpcTest::basic(ChainGetFinalizedTipset::request(())?),
561 ];
562
563 if !offline {
564 tests.extend([
565 validate_tagged_tipset_v2(
567 ChainGetTipSetV2::request((TipsetSelector {
568 key: None.into(),
569 height: None,
570 tag: Some(TipsetTag::Safe),
571 },))?,
572 offline,
573 ),
574 validate_tagged_tipset_v2(
576 ChainGetTipSetV2::request((TipsetSelector {
577 key: None.into(),
578 height: None,
579 tag: Some(TipsetTag::Finalized),
580 },))?,
581 offline,
582 ),
583 ]);
584 }
585
586 for block in tipset.block_headers() {
587 let block_cid = *block.cid();
588 tests.extend([
589 RpcTest::identity(ChainReadObj::request((block_cid,))?),
590 RpcTest::identity(ChainHasObj::request((block_cid,))?),
591 RpcTest::identity(ChainGetBlock::request((block_cid,))?),
592 RpcTest::identity(ChainGetBlockMessages::request((block_cid,))?),
593 RpcTest::identity(ChainGetParentMessages::request((block_cid,))?),
594 RpcTest::identity(ChainGetParentReceipts::request((block_cid,))?),
595 RpcTest::identity(ChainStatObj::request((block.messages, None))?),
596 RpcTest::identity(ChainStatObj::request((
597 block.messages,
598 Some(block.messages),
599 ))?),
600 ]);
601
602 let (bls_messages, secp_messages) = crate::chain::store::block_messages(&store, block)?;
603 for msg_cid in sample_message_cids(bls_messages.iter(), secp_messages.iter()) {
604 tests.extend([RpcTest::identity(ChainGetMessage::request((msg_cid,))?)]);
605 }
606
607 for receipt in Receipt::get_receipts(store, block.message_receipts)? {
608 if let Some(events_root) = receipt.events_root() {
609 tests.extend([RpcTest::identity(ChainGetEvents::request((events_root,))?)
610 .sort_policy(SortPolicy::All)]);
611 }
612 }
613 }
614
615 Ok(tests)
616}
617
618fn auth_tests() -> anyhow::Result<Vec<RpcTest>> {
619 Ok(vec![
621 RpcTest::basic(AuthNew::request((
622 AuthNewParams::process_perms(Permission::Admin.to_string())?,
623 None,
624 ))?),
625 RpcTest::basic(AuthNew::request((
626 AuthNewParams::process_perms(Permission::Sign.to_string())?,
627 None,
628 ))?),
629 RpcTest::basic(AuthNew::request((
630 AuthNewParams::process_perms(Permission::Write.to_string())?,
631 None,
632 ))?),
633 RpcTest::basic(AuthNew::request((
634 AuthNewParams::process_perms(Permission::Read.to_string())?,
635 None,
636 ))?),
637 ])
638}
639
640fn mpool_tests() -> Vec<RpcTest> {
641 vec![
642 RpcTest::identity(MpoolGetNonce::request((*KNOWN_CALIBNET_ADDRESS,)).unwrap()),
643 RpcTest::identity(MpoolGetNonce::request((*KNOWN_EMPTY_CALIBNET_ADDRESS,)).unwrap())
652 .policy_on_rejected(PolicyOnRejected::Pass),
653 RpcTest::basic(MpoolPending::request((ApiTipsetKey(None),)).unwrap()),
654 RpcTest::basic(MpoolSelect::request((ApiTipsetKey(None), TICKET_QUALITY_GREEDY)).unwrap()),
655 RpcTest::basic(MpoolSelect::request((ApiTipsetKey(None), TICKET_QUALITY_OPTIMAL)).unwrap())
656 .ignore("https://github.com/ChainSafe/forest/issues/4490"),
657 ]
658}
659
660fn mpool_tests_with_tipset(tipset: &Tipset) -> Vec<RpcTest> {
661 vec![
662 RpcTest::basic(MpoolPending::request((tipset.key().into(),)).unwrap()),
663 RpcTest::basic(MpoolSelect::request((tipset.key().into(), TICKET_QUALITY_GREEDY)).unwrap()),
664 RpcTest::basic(
665 MpoolSelect::request((tipset.key().into(), TICKET_QUALITY_OPTIMAL)).unwrap(),
666 )
667 .ignore("https://github.com/ChainSafe/forest/issues/4490"),
668 ]
669}
670
671fn net_tests() -> Vec<RpcTest> {
672 vec![
675 RpcTest::basic(NetAddrsListen::request(()).unwrap()),
676 RpcTest::basic(NetPeers::request(()).unwrap()),
677 RpcTest::identity(NetListening::request(()).unwrap()),
678 RpcTest::basic(NetAgentVersion::request((PeerId::random().to_string(),)).unwrap())
680 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
681 RpcTest::basic(NetFindPeer::request((PeerId::random().to_string(),)).unwrap())
682 .policy_on_rejected(PolicyOnRejected::Pass)
683 .ignore("It times out in lotus when peer not found"),
684 RpcTest::basic(NetInfo::request(()).unwrap())
685 .ignore("Not implemented in Lotus. Why do we even have this method?"),
686 RpcTest::basic(NetAutoNatStatus::request(()).unwrap()),
687 RpcTest::identity(NetVersion::request(()).unwrap()),
688 RpcTest::identity(NetProtectAdd::request((vec![PeerId::random().to_string()],)).unwrap()),
689 RpcTest::identity(
690 NetProtectRemove::request((vec![PeerId::random().to_string()],)).unwrap(),
691 ),
692 RpcTest::basic(NetProtectList::request(()).unwrap()),
693 ]
694}
695
696fn node_tests() -> Vec<RpcTest> {
697 vec![
698 ]
702}
703
704fn event_tests_with_tipset<DB: Blockstore>(_store: &Arc<DB>, tipset: &Tipset) -> Vec<RpcTest> {
705 let epoch = tipset.epoch();
706 vec![
707 RpcTest::identity(GetActorEventsRaw::request((None,)).unwrap())
708 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
709 RpcTest::identity(
710 GetActorEventsRaw::request((Some(ActorEventFilter {
711 addresses: vec![],
712 fields: Default::default(),
713 from_height: Some(epoch),
714 to_height: Some(epoch),
715 tipset_key: None,
716 }),))
717 .unwrap(),
718 )
719 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
720 .sort_policy(SortPolicy::All),
721 RpcTest::identity(
722 GetActorEventsRaw::request((Some(ActorEventFilter {
723 addresses: vec![],
724 fields: Default::default(),
725 from_height: Some(epoch - 100),
726 to_height: Some(epoch),
727 tipset_key: None,
728 }),))
729 .unwrap(),
730 )
731 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
732 .sort_policy(SortPolicy::All),
733 RpcTest::identity(
734 GetActorEventsRaw::request((Some(ActorEventFilter {
735 addresses: vec![],
736 fields: Default::default(),
737 from_height: None,
738 to_height: None,
739 tipset_key: Some(tipset.key().clone().into()),
740 }),))
741 .unwrap(),
742 )
743 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
744 .sort_policy(SortPolicy::All),
745 RpcTest::identity(
746 GetActorEventsRaw::request((Some(ActorEventFilter {
747 addresses: vec![
748 Address::from_str("t410fvtakbtytk4otbnfymn4zn5ow252nj7lcpbtersq")
749 .unwrap()
750 .into(),
751 ],
752 fields: Default::default(),
753 from_height: Some(epoch - 100),
754 to_height: Some(epoch),
755 tipset_key: None,
756 }),))
757 .unwrap(),
758 )
759 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
760 .sort_policy(SortPolicy::All),
761 {
762 use std::collections::BTreeMap;
763
764 use base64::{Engine, prelude::BASE64_STANDARD};
765
766 use crate::lotus_json::LotusJson;
767 use crate::rpc::misc::ActorEventBlock;
768
769 let topic = BASE64_STANDARD
770 .decode("0Gprf0kYSUs3GSF9GAJ4bB9REqbB2I/iz+wAtFhPauw=")
771 .unwrap();
772 let mut fields: BTreeMap<String, Vec<ActorEventBlock>> = Default::default();
773 fields.insert(
774 "t1".into(),
775 vec![ActorEventBlock {
776 codec: 85,
777 value: LotusJson(topic),
778 }],
779 );
780 RpcTest::identity(
781 GetActorEventsRaw::request((Some(ActorEventFilter {
782 addresses: vec![],
783 fields,
784 from_height: Some(epoch - 100),
785 to_height: Some(epoch),
786 tipset_key: None,
787 }),))
788 .unwrap(),
789 )
790 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError)
791 .sort_policy(SortPolicy::All)
792 },
793 ]
794}
795
796fn miner_tests_with_tipset<DB: Blockstore>(
797 store: &Arc<DB>,
798 tipset: &Tipset,
799 miner_address: Option<Address>,
800) -> anyhow::Result<Vec<RpcTest>> {
801 let Some(miner_address) = miner_address else {
803 return Ok(vec![]);
804 };
805
806 let mut tests = Vec::new();
807 for block in tipset.block_headers() {
808 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
809 tests.push(miner_create_block_test(
810 miner_address,
811 tipset,
812 bls_messages,
813 secp_messages,
814 ));
815 }
816 tests.push(miner_create_block_no_messages_test(miner_address, tipset));
817 Ok(tests)
818}
819
820fn miner_create_block_test(
821 miner: Address,
822 tipset: &Tipset,
823 bls_messages: Vec<Message>,
824 secp_messages: Vec<SignedMessage>,
825) -> RpcTest {
826 let priv_key = bls_signatures::PrivateKey::generate(&mut crate::utils::rand::forest_rng());
828 let signed_bls_msgs = bls_messages
829 .into_iter()
830 .map(|message| {
831 let sig = priv_key.sign(message.cid().to_bytes());
832 SignedMessage {
833 message,
834 signature: Signature::new_bls(sig.as_bytes().to_vec()),
835 }
836 })
837 .collect_vec();
838
839 let block_template = BlockTemplate {
840 miner,
841 parents: tipset.parents().to_owned(),
842 ticket: Ticket::default(),
843 eproof: ElectionProof::default(),
844 beacon_values: tipset.block_headers().first().beacon_entries.to_owned(),
845 messages: [signed_bls_msgs, secp_messages].concat(),
846 epoch: tipset.epoch(),
847 timestamp: tipset.min_timestamp(),
848 winning_post_proof: Vec::default(),
849 };
850 RpcTest::identity(MinerCreateBlock::request((block_template,)).unwrap())
851}
852
853fn miner_create_block_no_messages_test(miner: Address, tipset: &Tipset) -> RpcTest {
854 let block_template = BlockTemplate {
855 miner,
856 parents: tipset.parents().to_owned(),
857 ticket: Ticket::default(),
858 eproof: ElectionProof::default(),
859 beacon_values: tipset.block_headers().first().beacon_entries.to_owned(),
860 messages: Vec::default(),
861 epoch: tipset.epoch(),
862 timestamp: tipset.min_timestamp(),
863 winning_post_proof: Vec::default(),
864 };
865 RpcTest::identity(MinerCreateBlock::request((block_template,)).unwrap())
866}
867
868fn state_tests_with_tipset<DB: Blockstore>(
869 store: &Arc<DB>,
870 tipset: &Tipset,
871) -> anyhow::Result<Vec<RpcTest>> {
872 let mut tests = vec![
873 RpcTest::identity(StateNetworkName::request(())?),
874 RpcTest::identity(StateGetNetworkParams::request(())?),
875 RpcTest::identity(StateMinerInitialPledgeForSector::request((
876 1,
877 SectorSize::_32GiB,
878 1024,
879 tipset.key().into(),
880 ))?),
881 RpcTest::identity(StateGetActor::request((
882 Address::SYSTEM_ACTOR,
883 tipset.key().into(),
884 ))?),
885 RpcTest::identity(StateGetActorV2::request((
886 Address::SYSTEM_ACTOR,
887 TipsetSelector {
888 key: tipset.key().into(),
889 ..Default::default()
890 },
891 ))?),
892 RpcTest::identity(StateGetID::request((
893 Address::SYSTEM_ACTOR,
894 TipsetSelector {
895 key: tipset.key().into(),
896 ..Default::default()
897 },
898 ))?),
899 RpcTest::identity(StateGetRandomnessFromTickets::request((
900 DomainSeparationTag::ElectionProofProduction as i64,
901 tipset.epoch(),
902 "dead beef".as_bytes().to_vec(),
903 tipset.key().into(),
904 ))?),
905 RpcTest::identity(StateGetRandomnessDigestFromTickets::request((
906 tipset.epoch(),
907 tipset.key().into(),
908 ))?),
909 RpcTest::identity(StateGetRandomnessFromBeacon::request((
910 DomainSeparationTag::ElectionProofProduction as i64,
911 tipset.epoch(),
912 "dead beef".as_bytes().to_vec(),
913 tipset.key().into(),
914 ))?),
915 RpcTest::identity(StateGetRandomnessDigestFromBeacon::request((
916 tipset.epoch(),
917 tipset.key().into(),
918 ))?),
919 RpcTest::identity(StateLookupID::request((
921 Address::new_id(0xdeadbeef),
922 tipset.key().into(),
923 ))?),
924 RpcTest::identity(StateVerifiedRegistryRootKey::request((tipset
925 .key()
926 .into(),))?),
927 RpcTest::identity(StateVerifierStatus::request((
928 Address::VERIFIED_REGISTRY_ACTOR,
929 tipset.key().into(),
930 ))?),
931 RpcTest::identity(StateNetworkVersion::request((tipset.key().into(),))?),
932 RpcTest::identity(StateListMiners::request((tipset.key().into(),))?),
933 RpcTest::identity(StateListActors::request((tipset.key().into(),))?),
934 RpcTest::identity(MsigGetAvailableBalance::request((
935 Address::new_id(18101), tipset.key().into(),
937 ))?),
938 RpcTest::identity(MsigGetPending::request((
939 Address::new_id(18101), tipset.key().into(),
941 ))?),
942 RpcTest::identity(MsigGetVested::request((
943 Address::new_id(18101), tipset.parents().into(),
945 tipset.key().into(),
946 ))?),
947 RpcTest::identity(MsigGetVestingSchedule::request((
948 Address::new_id(18101), tipset.key().into(),
950 ))?),
951 RpcTest::identity(BeaconGetEntry::request((tipset.epoch(),))?),
952 RpcTest::identity(StateGetBeaconEntry::request((tipset.epoch(),))?),
953 RpcTest::identity(StateVerifiedClientStatus::request((
957 Address::VERIFIED_REGISTRY_ACTOR,
958 tipset.key().into(),
959 ))?),
960 RpcTest::identity(StateVerifiedClientStatus::request((
961 Address::DATACAP_TOKEN_ACTOR,
962 tipset.key().into(),
963 ))?),
964 RpcTest::identity(StateDealProviderCollateralBounds::request((
965 1,
966 true,
967 tipset.key().into(),
968 ))?),
969 RpcTest::identity(StateCirculatingSupply::request((tipset.key().into(),))?),
970 RpcTest::identity(StateVMCirculatingSupplyInternal::request((tipset
971 .key()
972 .into(),))?),
973 RpcTest::identity(StateMarketParticipants::request((tipset.key().into(),))?),
974 RpcTest::identity(StateMarketDeals::request((tipset.key().into(),))?),
975 RpcTest::identity(StateSectorPreCommitInfo::request((
976 Default::default(), u16::MAX as _,
978 tipset.key().into(),
979 ))?)
980 .policy_on_rejected(PolicyOnRejected::Pass),
981 RpcTest::identity(StateSectorGetInfo::request((
982 Default::default(), u16::MAX as _, tipset.key().into(),
985 ))?)
986 .policy_on_rejected(PolicyOnRejected::Pass),
987 RpcTest::identity(StateGetAllocationIdForPendingDeal::request((
988 u16::MAX as _, tipset.key().into(),
990 ))?),
991 RpcTest::identity(StateGetAllocationForPendingDeal::request((
992 u16::MAX as _, tipset.key().into(),
994 ))?),
995 RpcTest::identity(StateCompute::request((
996 tipset.epoch(),
997 vec![],
998 tipset.key().into(),
999 ))?),
1000 ];
1001
1002 tests.extend(read_state_api_tests(tipset)?);
1003 tests.extend(create_all_state_decode_params_tests(tipset)?);
1004
1005 for &pending_deal_id in
1006 StateGetAllocationIdForPendingDeal::get_allocations_for_pending_deals(store, tipset)?
1007 .keys()
1008 .take(COLLECTION_SAMPLE_SIZE)
1009 {
1010 tests.extend([
1011 RpcTest::identity(StateGetAllocationIdForPendingDeal::request((
1012 pending_deal_id,
1013 tipset.key().into(),
1014 ))?),
1015 RpcTest::identity(StateGetAllocationForPendingDeal::request((
1016 pending_deal_id,
1017 tipset.key().into(),
1018 ))?),
1019 ]);
1020 }
1021
1022 let (deals, deals_map) = {
1024 let state = StateTree::new_from_root(store.clone(), tipset.parent_state())?;
1025 let actor = state.get_required_actor(&Address::MARKET_ACTOR)?;
1026 let market_state = market::State::load(&store, actor.code, actor.state)?;
1027 let proposals = market_state.proposals(&store)?;
1028 let mut deals = vec![];
1029 let mut deals_map = HashMap::default();
1030 proposals.for_each(|deal_id, deal_proposal| {
1031 deals.push(deal_id);
1032 deals_map.insert(deal_id, deal_proposal);
1033 Ok(())
1034 })?;
1035 (deals, deals_map)
1036 };
1037
1038 for deal in deals.into_iter().take(COLLECTION_SAMPLE_SIZE) {
1040 tests.push(RpcTest::identity(StateMarketStorageDeal::request((
1041 deal,
1042 tipset.key().into(),
1043 ))?));
1044 }
1045
1046 for block in tipset.block_headers() {
1047 tests.extend([
1048 RpcTest::identity(StateMinerAllocated::request((
1049 block.miner_address,
1050 tipset.key().into(),
1051 ))?),
1052 RpcTest::identity(StateMinerActiveSectors::request((
1053 block.miner_address,
1054 tipset.key().into(),
1055 ))?),
1056 RpcTest::identity(StateLookupID::request((
1057 block.miner_address,
1058 tipset.key().into(),
1059 ))?),
1060 RpcTest::identity(StateLookupRobustAddress::request((
1061 block.miner_address,
1062 tipset.key().into(),
1063 ))?),
1064 RpcTest::identity(StateMinerSectors::request((
1065 block.miner_address,
1066 None,
1067 tipset.key().into(),
1068 ))?),
1069 RpcTest::identity(StateMinerPartitions::request((
1070 block.miner_address,
1071 0,
1072 tipset.key().into(),
1073 ))?),
1074 RpcTest::identity(StateMarketBalance::request((
1075 block.miner_address,
1076 tipset.key().into(),
1077 ))?),
1078 RpcTest::identity(StateMinerInfo::request((
1079 block.miner_address,
1080 tipset.key().into(),
1081 ))?),
1082 RpcTest::identity(StateMinerPower::request((
1083 block.miner_address,
1084 tipset.key().into(),
1085 ))?),
1086 RpcTest::identity(StateMinerDeadlines::request((
1087 block.miner_address,
1088 tipset.key().into(),
1089 ))?),
1090 RpcTest::identity(StateMinerProvingDeadline::request((
1091 block.miner_address,
1092 tipset.key().into(),
1093 ))?),
1094 RpcTest::identity(StateMinerAvailableBalance::request((
1095 block.miner_address,
1096 tipset.key().into(),
1097 ))?),
1098 RpcTest::identity(StateMinerFaults::request((
1099 block.miner_address,
1100 tipset.key().into(),
1101 ))?),
1102 RpcTest::identity(MinerGetBaseInfo::request((
1103 block.miner_address,
1104 block.epoch,
1105 tipset.key().into(),
1106 ))?),
1107 RpcTest::identity(StateMinerRecoveries::request((
1108 block.miner_address,
1109 tipset.key().into(),
1110 ))?),
1111 RpcTest::identity(StateMinerSectorCount::request((
1112 block.miner_address,
1113 tipset.key().into(),
1114 ))?),
1115 RpcTest::identity(StateGetClaims::request((
1116 block.miner_address,
1117 tipset.key().into(),
1118 ))?),
1119 RpcTest::identity(StateGetAllClaims::request((tipset.key().into(),))?),
1120 RpcTest::identity(StateGetAllAllocations::request((tipset.key().into(),))?),
1121 RpcTest::identity(StateSectorPreCommitInfo::request((
1122 block.miner_address,
1123 u16::MAX as _, tipset.key().into(),
1125 ))?)
1126 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1127 RpcTest::identity(StateSectorGetInfo::request((
1128 block.miner_address,
1129 u16::MAX as _, tipset.key().into(),
1131 ))?)
1132 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1133 ]);
1134 for claim_id in StateGetClaims::get_claims(store, &block.miner_address, tipset)?
1135 .keys()
1136 .take(COLLECTION_SAMPLE_SIZE)
1137 {
1138 tests.extend([RpcTest::identity(StateGetClaim::request((
1139 block.miner_address,
1140 *claim_id,
1141 tipset.key().into(),
1142 ))?)]);
1143 }
1144 for address in StateGetAllocations::get_valid_actor_addresses(store, tipset)?
1145 .take(COLLECTION_SAMPLE_SIZE)
1146 {
1147 tests.extend([RpcTest::identity(StateGetAllocations::request((
1148 address,
1149 tipset.key().into(),
1150 ))?)]);
1151 for allocation_id in StateGetAllocations::get_allocations(store, &address, tipset)?
1152 .keys()
1153 .take(COLLECTION_SAMPLE_SIZE)
1154 {
1155 tests.extend([RpcTest::identity(StateGetAllocation::request((
1156 address,
1157 *allocation_id,
1158 tipset.key().into(),
1159 ))?)]);
1160 }
1161 }
1162 for sector in StateSectorGetInfo::get_sectors(store, &block.miner_address, tipset)?
1163 .into_iter()
1164 .take(COLLECTION_SAMPLE_SIZE)
1165 {
1166 tests.extend([
1167 RpcTest::identity(StateSectorGetInfo::request((
1168 block.miner_address,
1169 sector,
1170 tipset.key().into(),
1171 ))?),
1172 RpcTest::identity(StateMinerSectors::request((
1173 block.miner_address,
1174 {
1175 let mut bf = BitField::new();
1176 bf.set(sector);
1177 Some(bf)
1178 },
1179 tipset.key().into(),
1180 ))?),
1181 RpcTest::identity(StateSectorExpiration::request((
1182 block.miner_address,
1183 sector,
1184 tipset.key().into(),
1185 ))?)
1186 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1187 RpcTest::identity(StateSectorPartition::request((
1188 block.miner_address,
1189 sector,
1190 tipset.key().into(),
1191 ))?),
1192 RpcTest::identity(StateMinerSectorAllocated::request((
1193 block.miner_address,
1194 sector,
1195 tipset.key().into(),
1196 ))?),
1197 ]);
1198 }
1199 for sector in StateSectorPreCommitInfo::get_sectors(store, &block.miner_address, tipset)?
1200 .into_iter()
1201 .take(COLLECTION_SAMPLE_SIZE)
1202 {
1203 tests.extend([RpcTest::identity(StateSectorPreCommitInfo::request((
1204 block.miner_address,
1205 sector,
1206 tipset.key().into(),
1207 ))?)]);
1208 }
1209 for info in StateSectorPreCommitInfo::get_sector_pre_commit_infos(
1210 store,
1211 &block.miner_address,
1212 tipset,
1213 )?
1214 .into_iter()
1215 .take(COLLECTION_SAMPLE_SIZE)
1216 .filter(|info| {
1217 !info.deal_ids.iter().any(|id| {
1218 if let Some(Ok(deal)) = deals_map.get(id) {
1219 tipset.epoch() > deal.start_epoch || info.expiration > deal.end_epoch
1220 } else {
1221 true
1222 }
1223 })
1224 }) {
1225 tests.extend([RpcTest::identity(
1226 StateMinerInitialPledgeCollateral::request((
1227 block.miner_address,
1228 info.clone(),
1229 tipset.key().into(),
1230 ))?,
1231 )]);
1232 tests.extend([RpcTest::identity(
1233 StateMinerPreCommitDepositForPower::request((
1234 block.miner_address,
1235 info,
1236 tipset.key().into(),
1237 ))?,
1238 )]);
1239 }
1240
1241 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
1242 for msg_cid in sample_message_cids(bls_messages.iter(), secp_messages.iter()) {
1243 tests.extend([
1244 RpcTest::identity(StateReplay::request((tipset.key().into(), msg_cid))?),
1245 validate_message_lookup(
1246 StateWaitMsg::request((msg_cid, 0, 10101, true))?
1247 .with_timeout(Duration::from_secs(15)),
1248 ),
1249 validate_message_lookup(
1250 StateWaitMsg::request((msg_cid, 0, 10101, false))?
1251 .with_timeout(Duration::from_secs(15)),
1252 ),
1253 validate_message_lookup(StateSearchMsg::request((
1254 None.into(),
1255 msg_cid,
1256 800,
1257 true,
1258 ))?),
1259 validate_message_lookup(StateSearchMsg::request((
1260 None.into(),
1261 msg_cid,
1262 800,
1263 false,
1264 ))?),
1265 validate_message_lookup(StateSearchMsgLimited::request((msg_cid, 800))?),
1266 ]);
1267 }
1268 for msg in sample_messages(bls_messages.iter(), secp_messages.iter()) {
1269 tests.extend([
1270 RpcTest::identity(StateAccountKey::request((msg.from(), tipset.key().into()))?),
1271 RpcTest::identity(StateAccountKey::request((msg.from(), Default::default()))?),
1272 RpcTest::identity(StateLookupID::request((msg.from(), tipset.key().into()))?),
1273 RpcTest::identity(StateListMessages::request((
1274 MessageFilter {
1275 from: Some(msg.from()),
1276 to: Some(msg.to()),
1277 },
1278 tipset.key().into(),
1279 tipset.epoch(),
1280 ))?),
1281 RpcTest::identity(StateListMessages::request((
1282 MessageFilter {
1283 from: Some(msg.from()),
1284 to: None,
1285 },
1286 tipset.key().into(),
1287 tipset.epoch(),
1288 ))?),
1289 RpcTest::identity(StateListMessages::request((
1290 MessageFilter {
1291 from: None,
1292 to: Some(msg.to()),
1293 },
1294 tipset.key().into(),
1295 tipset.epoch(),
1296 ))?),
1297 RpcTest::identity(StateCall::request((msg.clone(), tipset.key().into()))?),
1298 ]);
1299 }
1300 }
1301
1302 Ok(tests)
1303}
1304
1305fn wallet_tests(worker_address: Option<Address>) -> Vec<RpcTest> {
1306 let prefunded_wallets = [
1307 *KNOWN_CALIBNET_F0_ADDRESS,
1309 *KNOWN_CALIBNET_F1_ADDRESS,
1310 *KNOWN_CALIBNET_F2_ADDRESS,
1311 *KNOWN_CALIBNET_F3_ADDRESS,
1312 *KNOWN_CALIBNET_F4_ADDRESS,
1313 *KNOWN_EMPTY_CALIBNET_ADDRESS,
1315 ];
1316
1317 let mut tests = vec![];
1318 for wallet in prefunded_wallets {
1319 tests.push(RpcTest::identity(
1320 WalletBalance::request((wallet,)).unwrap(),
1321 ));
1322 tests.push(RpcTest::identity(
1323 WalletValidateAddress::request((wallet.to_string(),)).unwrap(),
1324 ));
1325 }
1326
1327 let known_wallet = *KNOWN_CALIBNET_ADDRESS;
1328 let signature = "44364ca78d85e53dda5ac6f719a4f2de3261c17f58558ab7730f80c478e6d43775244e7d6855afad82e4a1fd6449490acfa88e3fcfe7c1fe96ed549c100900b400";
1330 let text = "Hello world!".as_bytes().to_vec();
1331 let sig_bytes = hex::decode(signature).unwrap();
1332 let signature = match known_wallet.protocol() {
1333 Protocol::Secp256k1 => Signature::new_secp256k1(sig_bytes),
1334 Protocol::BLS => Signature::new_bls(sig_bytes),
1335 _ => panic!("Invalid signature (must be bls or secp256k1)"),
1336 };
1337
1338 tests.push(RpcTest::identity(
1339 WalletBalance::request((known_wallet,)).unwrap(),
1340 ));
1341 tests.push(RpcTest::identity(
1342 WalletValidateAddress::request((known_wallet.to_string(),)).unwrap(),
1343 ));
1344 tests.push(
1345 RpcTest::identity(
1346 WalletValidateAddress::request((
1348 "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn".to_string(),
1349 ))
1350 .unwrap(),
1351 )
1352 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalErrorCaseInsensitive),
1354 );
1355 tests.push(RpcTest::identity(
1356 WalletVerify::request((known_wallet, text, signature)).unwrap(),
1357 ));
1358
1359 if let Some(worker_address) = worker_address {
1362 use base64::{Engine, prelude::BASE64_STANDARD};
1363 let msg =
1364 BASE64_STANDARD.encode("Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn".as_bytes());
1365 tests.push(RpcTest::identity(
1366 WalletSign::request((worker_address, msg.into())).unwrap(),
1367 ));
1368 tests.push(RpcTest::identity(
1369 WalletSign::request((worker_address, Vec::new())).unwrap(),
1370 ));
1371 let msg: Message = Message {
1372 from: worker_address,
1373 to: worker_address,
1374 value: TokenAmount::from_whole(1),
1375 method_num: METHOD_SEND,
1376 ..Default::default()
1377 };
1378 tests.push(RpcTest::identity(
1379 WalletSignMessage::request((worker_address, msg)).unwrap(),
1380 ));
1381 }
1382 tests
1383}
1384
1385fn eth_tests() -> Vec<RpcTest> {
1386 let mut tests = vec![];
1387 for use_alias in [false, true] {
1388 tests.push(RpcTest::identity(
1389 EthAccounts::request_with_alias((), use_alias).unwrap(),
1390 ));
1391 tests.push(RpcTest::basic(
1392 EthBlockNumber::request_with_alias((), use_alias).unwrap(),
1393 ));
1394 tests.push(RpcTest::identity(
1395 EthChainId::request_with_alias((), use_alias).unwrap(),
1396 ));
1397 tests.push(RpcTest::validate(
1399 EthGasPrice::request_with_alias((), use_alias).unwrap(),
1400 |forest, lotus| forest.0.is_positive() && lotus.0.is_positive(),
1401 ));
1402 tests.push(RpcTest::basic(
1403 EthSyncing::request_with_alias((), use_alias).unwrap(),
1404 ));
1405 tests.push(RpcTest::identity(
1406 EthGetBalance::request_with_alias(
1407 (
1408 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1409 BlockNumberOrHash::from_predefined(Predefined::Latest),
1410 ),
1411 use_alias,
1412 )
1413 .unwrap(),
1414 ));
1415 tests.push(RpcTest::identity(
1416 EthGetBalance::request_with_alias(
1417 (
1418 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1419 BlockNumberOrHash::from_predefined(Predefined::Pending),
1420 ),
1421 use_alias,
1422 )
1423 .unwrap(),
1424 ));
1425 tests.push(RpcTest::basic(
1426 Web3ClientVersion::request_with_alias((), use_alias).unwrap(),
1427 ));
1428 tests.push(RpcTest::basic(
1429 EthMaxPriorityFeePerGas::request_with_alias((), use_alias).unwrap(),
1430 ));
1431 tests.push(RpcTest::identity(
1432 EthProtocolVersion::request_with_alias((), use_alias).unwrap(),
1433 ));
1434
1435 let cases = [
1436 (
1437 Some(EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap()),
1438 Some(
1439 "0xf8b2cb4f000000000000000000000000CbfF24DED1CE6B53712078759233Ac8f91ea71B6"
1440 .parse()
1441 .unwrap(),
1442 ),
1443 ),
1444 (Some(EthAddress::from_str(ZERO_ADDRESS).unwrap()), None),
1445 (
1448 None,
1449 Some(
1450 EthBytes::from_str(
1451 concat!("0x", include_str!("contracts/cthulhu/invoke.hex")).trim(),
1452 )
1453 .unwrap(),
1454 ),
1455 ),
1456 ];
1457
1458 for (to, data) in cases {
1459 let msg = EthCallMessage {
1460 to: to.clone(),
1461 data: data.clone(),
1462 ..EthCallMessage::default()
1463 };
1464
1465 tests.push(RpcTest::identity(
1466 EthCall::request_with_alias(
1467 (
1468 msg.clone(),
1469 BlockNumberOrHash::from_predefined(Predefined::Latest),
1470 ),
1471 use_alias,
1472 )
1473 .unwrap(),
1474 ));
1475
1476 for tag in [
1477 ExtPredefined::Latest,
1478 ExtPredefined::Safe,
1479 ExtPredefined::Finalized,
1480 ] {
1481 tests.push(RpcTest::identity(
1482 EthCallV2::request_with_alias(
1483 (msg.clone(), ExtBlockNumberOrHash::PredefinedBlock(tag)),
1484 use_alias,
1485 )
1486 .unwrap(),
1487 ));
1488 }
1489 }
1490
1491 let cases = [
1492 Some(EthAddressList::List(vec![])),
1493 Some(EthAddressList::List(vec![
1494 EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(),
1495 EthAddress::from_str("0x89beb26addec4bc7e9f475aacfd084300d6de719").unwrap(),
1496 ])),
1497 Some(EthAddressList::Single(
1498 EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(),
1499 )),
1500 None,
1501 ];
1502
1503 for address in cases {
1504 tests.push(RpcTest::basic(
1505 EthNewFilter::request_with_alias(
1506 (EthFilterSpec {
1507 address,
1508 ..Default::default()
1509 },),
1510 use_alias,
1511 )
1512 .unwrap(),
1513 ));
1514 }
1515 tests.push(RpcTest::basic(
1516 EthNewPendingTransactionFilter::request_with_alias((), use_alias).unwrap(),
1517 ));
1518 tests.push(RpcTest::basic(
1519 EthNewBlockFilter::request_with_alias((), use_alias).unwrap(),
1520 ));
1521 tests.push(RpcTest::identity(
1522 EthUninstallFilter::request_with_alias((FilterID::new().unwrap(),), use_alias).unwrap(),
1523 ));
1524 tests.push(RpcTest::identity(
1525 EthAddressToFilecoinAddress::request(("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa"
1526 .parse()
1527 .unwrap(),))
1528 .unwrap(),
1529 ));
1530 tests.push(RpcTest::identity(
1531 FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F0_ADDRESS, None)).unwrap(),
1532 ));
1533 tests.push(RpcTest::identity(
1534 FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F1_ADDRESS, None)).unwrap(),
1535 ));
1536 tests.push(RpcTest::identity(
1537 FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F2_ADDRESS, None)).unwrap(),
1538 ));
1539 tests.push(RpcTest::identity(
1540 FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F3_ADDRESS, None)).unwrap(),
1541 ));
1542 tests.push(RpcTest::identity(
1543 FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F4_ADDRESS, None)).unwrap(),
1544 ));
1545 }
1546 tests
1547}
1548
1549fn eth_call_api_err_tests(epoch: i64) -> Vec<RpcTest> {
1550 let contract_codes = [
1551 include_str!("./contracts/arithmetic_err/arithmetic_overflow_err.hex"),
1552 include_str!("contracts/assert_err/assert_err.hex"),
1553 include_str!("./contracts/divide_by_zero_err/divide_by_zero_err.hex"),
1554 include_str!("./contracts/generic_panic_err/generic_panic_err.hex"),
1555 include_str!("./contracts/index_out_of_bounds_err/index_out_of_bounds_err.hex"),
1556 include_str!("./contracts/invalid_enum_err/invalid_enum_err.hex"),
1557 include_str!("./contracts/invalid_storage_array_err/invalid_storage_array_err.hex"),
1558 include_str!("./contracts/out_of_memory_err/out_of_memory_err.hex"),
1559 include_str!("./contracts/pop_empty_array_err/pop_empty_array_err.hex"),
1560 include_str!("./contracts/uninitialized_fn_err/uninitialized_fn_err.hex"),
1561 ];
1562
1563 let mut tests = Vec::new();
1564
1565 for &contract_hex in &contract_codes {
1566 let contract_code =
1567 EthBytes::from_str(contract_hex).expect("Contract bytecode should be valid hex");
1568
1569 let zero_address = EthAddress::from_str(ZERO_ADDRESS).unwrap();
1570 let msg = EthCallMessage {
1572 from: Some(zero_address),
1573 data: Some(contract_code),
1574 ..EthCallMessage::default()
1575 };
1576
1577 let eth_call_request =
1578 EthCall::request((msg.clone(), BlockNumberOrHash::from_block_number(epoch))).unwrap();
1579
1580 tests.push(
1581 RpcTest::identity(eth_call_request)
1582 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1583 );
1584
1585 let eth_call_v2_request =
1586 EthCallV2::request((msg, ExtBlockNumberOrHash::from_block_number(epoch))).unwrap();
1587
1588 tests.push(
1589 RpcTest::identity(eth_call_v2_request)
1590 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1591 );
1592 }
1593
1594 tests
1595}
1596
1597fn eth_tests_with_tipset<DB: Blockstore>(store: &Arc<DB>, shared_tipset: &Tipset) -> Vec<RpcTest> {
1598 let block_cid = shared_tipset.key().cid().unwrap();
1599 let block_hash: EthHash = block_cid.into();
1600
1601 let mut tests = vec![
1602 RpcTest::identity(
1603 EthGetBalance::request((
1604 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1605 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1606 ))
1607 .unwrap(),
1608 ),
1609 RpcTest::identity(
1610 EthGetBalance::request((
1611 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1612 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1613 ))
1614 .unwrap(),
1615 ),
1616 RpcTest::identity(
1617 EthGetBalance::request((
1618 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1619 BlockNumberOrHash::from_block_number_object(shared_tipset.epoch()),
1620 ))
1621 .unwrap(),
1622 ),
1623 RpcTest::identity(
1624 EthGetBalance::request((
1625 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1626 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), false),
1627 ))
1628 .unwrap(),
1629 ),
1630 RpcTest::identity(
1631 EthGetBalance::request((
1632 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1633 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1634 ))
1635 .unwrap(),
1636 ),
1637 RpcTest::identity(
1638 EthGetBalance::request((
1639 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1640 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1641 ))
1642 .unwrap(),
1643 )
1644 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1645 RpcTest::basic(
1646 EthGetBalance::request((
1647 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1648 BlockNumberOrHash::from_predefined(Predefined::Pending),
1649 ))
1650 .unwrap(),
1651 ),
1652 RpcTest::basic(
1653 EthGetBalance::request((
1654 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1655 BlockNumberOrHash::from_predefined(Predefined::Latest),
1656 ))
1657 .unwrap(),
1658 ),
1659 RpcTest::identity(
1660 EthGetBalance::request((
1661 generate_eth_random_address().unwrap(),
1662 BlockNumberOrHash::from_predefined(Predefined::Latest),
1663 ))
1664 .unwrap(),
1665 ),
1666 RpcTest::identity(
1667 EthGetBalanceV2::request((
1668 EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(),
1669 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1670 ))
1671 .unwrap(),
1672 ),
1673 RpcTest::identity(
1674 EthGetBalanceV2::request((
1675 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1676 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
1677 ))
1678 .unwrap(),
1679 ),
1680 RpcTest::identity(
1681 EthGetBalanceV2::request((
1682 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1683 ExtBlockNumberOrHash::from_block_number_object(shared_tipset.epoch()),
1684 ))
1685 .unwrap(),
1686 ),
1687 RpcTest::identity(
1688 EthGetBalanceV2::request((
1689 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1690 ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), false),
1691 ))
1692 .unwrap(),
1693 ),
1694 RpcTest::identity(
1695 EthGetBalanceV2::request((
1696 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1697 ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1698 ))
1699 .unwrap(),
1700 ),
1701 RpcTest::identity(
1702 EthGetBalanceV2::request((
1703 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1704 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest),
1705 ))
1706 .unwrap(),
1707 )
1708 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1709 RpcTest::basic(
1710 EthGetBalanceV2::request((
1711 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1712 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending),
1713 ))
1714 .unwrap(),
1715 ),
1716 RpcTest::basic(
1717 EthGetBalanceV2::request((
1718 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1719 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
1720 ))
1721 .unwrap(),
1722 ),
1723 RpcTest::basic(
1724 EthGetBalanceV2::request((
1725 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1726 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
1727 ))
1728 .unwrap(),
1729 ),
1730 RpcTest::basic(
1731 EthGetBalanceV2::request((
1732 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1733 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
1734 ))
1735 .unwrap(),
1736 ),
1737 RpcTest::identity(
1738 EthGetBalanceV2::request((
1739 generate_eth_random_address().unwrap(),
1740 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
1741 ))
1742 .unwrap(),
1743 ),
1744 RpcTest::identity(
1745 EthGetBlockByNumber::request((
1746 BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())),
1747 false,
1748 ))
1749 .unwrap(),
1750 ),
1751 RpcTest::identity(
1752 EthGetBlockByNumber::request((
1753 BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())),
1754 true,
1755 ))
1756 .unwrap(),
1757 ),
1758 RpcTest::identity(
1759 EthGetBlockByNumber::request((
1760 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
1761 true,
1762 ))
1763 .unwrap(),
1764 )
1765 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1766 RpcTest::basic(
1767 EthGetBlockByNumber::request((
1768 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending),
1769 true,
1770 ))
1771 .unwrap(),
1772 ),
1773 RpcTest::basic(
1774 EthGetBlockByNumber::request((
1775 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest),
1776 true,
1777 ))
1778 .unwrap(),
1779 ),
1780 RpcTest::basic(
1781 EthGetBlockByNumber::request((
1782 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
1783 true,
1784 ))
1785 .unwrap(),
1786 ),
1787 RpcTest::basic(
1788 EthGetBlockByNumber::request((
1789 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
1790 true,
1791 ))
1792 .unwrap(),
1793 ),
1794 RpcTest::identity(
1795 EthGetBlockByNumberV2::request((
1796 BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())),
1797 false,
1798 ))
1799 .unwrap(),
1800 ),
1801 RpcTest::identity(
1802 EthGetBlockByNumberV2::request((
1803 BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())),
1804 true,
1805 ))
1806 .unwrap(),
1807 ),
1808 RpcTest::identity(
1809 EthGetBlockByNumberV2::request((
1810 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
1811 true,
1812 ))
1813 .unwrap(),
1814 )
1815 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1816 RpcTest::basic(
1817 EthGetBlockByNumberV2::request((
1818 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending),
1819 true,
1820 ))
1821 .unwrap(),
1822 ),
1823 RpcTest::basic(
1824 EthGetBlockByNumberV2::request((
1825 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest),
1826 true,
1827 ))
1828 .unwrap(),
1829 ),
1830 RpcTest::basic(
1831 EthGetBlockByNumberV2::request((
1832 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
1833 true,
1834 ))
1835 .unwrap(),
1836 ),
1837 RpcTest::basic(
1838 EthGetBlockByNumberV2::request((
1839 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
1840 true,
1841 ))
1842 .unwrap(),
1843 ),
1844 RpcTest::identity(
1845 EthGetBlockReceipts::request((BlockNumberOrHash::from_block_hash_object(
1846 block_hash.clone(),
1847 true,
1848 ),))
1849 .unwrap(),
1850 ),
1851 RpcTest::basic(
1855 EthGetBlockReceipts::request((BlockNumberOrHash::from_predefined(Predefined::Latest),))
1856 .unwrap(),
1857 ),
1858 RpcTest::identity(
1859 EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_block_hash_object(
1860 block_hash.clone(),
1861 true,
1862 ),))
1863 .unwrap(),
1864 ),
1865 RpcTest::basic(
1866 EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_predefined(
1867 ExtPredefined::Safe,
1868 ),))
1869 .unwrap(),
1870 ),
1871 RpcTest::basic(
1872 EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_predefined(
1873 ExtPredefined::Finalized,
1874 ),))
1875 .unwrap(),
1876 ),
1877 RpcTest::identity(
1878 EthGetBlockTransactionCountByHash::request((block_hash.clone(),)).unwrap(),
1879 ),
1880 RpcTest::identity(
1881 EthGetBlockReceiptsLimited::request((
1882 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1883 4,
1884 ))
1885 .unwrap(),
1886 )
1887 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1888 RpcTest::identity(
1889 EthGetBlockReceiptsLimited::request((
1890 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1891 -1,
1892 ))
1893 .unwrap(),
1894 ),
1895 RpcTest::identity(
1896 EthGetBlockReceiptsLimitedV2::request((
1897 ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1898 4,
1899 ))
1900 .unwrap(),
1901 )
1902 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1903 RpcTest::identity(
1904 EthGetBlockReceiptsLimitedV2::request((
1905 ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1906 -1,
1907 ))
1908 .unwrap(),
1909 ),
1910 RpcTest::identity(
1911 EthGetBlockTransactionCountByNumber::request((EthInt64(shared_tipset.epoch()),))
1912 .unwrap(),
1913 ),
1914 RpcTest::identity(
1915 EthGetBlockTransactionCountByNumberV2::request((BlockNumberOrPredefined::BlockNumber(
1916 EthInt64(shared_tipset.epoch()),
1917 ),))
1918 .unwrap(),
1919 ),
1920 RpcTest::identity(
1921 EthGetBlockTransactionCountByNumberV2::request((
1922 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
1923 ))
1924 .unwrap(),
1925 ),
1926 RpcTest::identity(
1927 EthGetBlockTransactionCountByNumberV2::request((
1928 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
1929 ))
1930 .unwrap(),
1931 ),
1932 RpcTest::identity(
1933 EthGetTransactionCount::request((
1934 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1935 BlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1936 ))
1937 .unwrap(),
1938 ),
1939 RpcTest::identity(
1940 EthGetTransactionCount::request((
1941 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1942 BlockNumberOrHash::from_predefined(Predefined::Earliest),
1943 ))
1944 .unwrap(),
1945 )
1946 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1947 RpcTest::basic(
1948 EthGetTransactionCount::request((
1949 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1950 BlockNumberOrHash::from_predefined(Predefined::Pending),
1951 ))
1952 .unwrap(),
1953 ),
1954 RpcTest::basic(
1955 EthGetTransactionCount::request((
1956 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1957 BlockNumberOrHash::from_predefined(Predefined::Latest),
1958 ))
1959 .unwrap(),
1960 ),
1961 RpcTest::identity(
1962 EthGetTransactionCount::request((
1963 generate_eth_random_address().unwrap(),
1964 BlockNumberOrHash::from_predefined(Predefined::Latest),
1965 ))
1966 .unwrap(),
1967 ),
1968 RpcTest::identity(
1969 EthGetTransactionCountV2::request((
1970 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1971 ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), true),
1972 ))
1973 .unwrap(),
1974 ),
1975 RpcTest::identity(
1976 EthGetTransactionCountV2::request((
1977 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1978 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest),
1979 ))
1980 .unwrap(),
1981 )
1982 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
1983 RpcTest::basic(
1984 EthGetTransactionCountV2::request((
1985 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1986 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending),
1987 ))
1988 .unwrap(),
1989 ),
1990 RpcTest::basic(
1991 EthGetTransactionCountV2::request((
1992 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
1993 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
1994 ))
1995 .unwrap(),
1996 ),
1997 RpcTest::basic(
1998 EthGetTransactionCountV2::request((
1999 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
2000 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
2001 ))
2002 .unwrap(),
2003 ),
2004 RpcTest::basic(
2005 EthGetTransactionCountV2::request((
2006 EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(),
2007 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
2008 ))
2009 .unwrap(),
2010 ),
2011 RpcTest::identity(
2012 EthGetTransactionCountV2::request((
2013 generate_eth_random_address().unwrap(),
2014 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
2015 ))
2016 .unwrap(),
2017 ),
2018 RpcTest::identity(
2019 EthGetStorageAt::request((
2020 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2022 EthBytes(vec![0xa]),
2023 BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())),
2024 ))
2025 .unwrap(),
2026 ),
2027 RpcTest::identity(
2028 EthGetStorageAt::request((
2029 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2030 EthBytes(vec![0xa]),
2031 BlockNumberOrHash::from_predefined(Predefined::Earliest),
2032 ))
2033 .unwrap(),
2034 )
2035 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2036 RpcTest::basic(
2037 EthGetStorageAt::request((
2038 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2039 EthBytes(vec![0xa]),
2040 BlockNumberOrHash::from_predefined(Predefined::Pending),
2041 ))
2042 .unwrap(),
2043 ),
2044 RpcTest::basic(
2045 EthGetStorageAt::request((
2046 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2047 EthBytes(vec![0xa]),
2048 BlockNumberOrHash::from_predefined(Predefined::Latest),
2049 ))
2050 .unwrap(),
2051 ),
2052 RpcTest::identity(
2053 EthGetStorageAt::request((
2054 generate_eth_random_address().unwrap(),
2055 EthBytes(vec![0x0]),
2056 BlockNumberOrHash::from_predefined(Predefined::Latest),
2057 ))
2058 .unwrap(),
2059 ),
2060 RpcTest::identity(
2061 EthGetStorageAtV2::request((
2062 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2063 EthBytes(vec![0xa]),
2064 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2065 ))
2066 .unwrap(),
2067 ),
2068 RpcTest::basic(
2069 EthGetStorageAtV2::request((
2070 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2071 EthBytes(vec![0xa]),
2072 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
2073 ))
2074 .unwrap(),
2075 ),
2076 RpcTest::basic(
2077 EthGetStorageAtV2::request((
2078 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2079 EthBytes(vec![0xa]),
2080 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
2081 ))
2082 .unwrap(),
2083 ),
2084 RpcTest::identity(
2085 EthGetStorageAtV2::request((
2086 generate_eth_random_address().unwrap(),
2087 EthBytes(vec![0x0]),
2088 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
2089 ))
2090 .unwrap(),
2091 ),
2092 RpcTest::identity(
2093 EthFeeHistory::request((
2094 10.into(),
2095 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
2096 None,
2097 ))
2098 .unwrap(),
2099 ),
2100 RpcTest::identity(
2101 EthFeeHistory::request((
2102 10.into(),
2103 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
2104 Some(vec![10., 50., 90.]),
2105 ))
2106 .unwrap(),
2107 ),
2108 RpcTest::identity(
2109 EthFeeHistory::request((
2110 10.into(),
2111 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
2112 None,
2113 ))
2114 .unwrap(),
2115 )
2116 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2117 RpcTest::basic(
2118 EthFeeHistory::request((
2119 10.into(),
2120 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending),
2121 Some(vec![10., 50., 90.]),
2122 ))
2123 .unwrap(),
2124 ),
2125 RpcTest::basic(
2126 EthFeeHistory::request((
2127 10.into(),
2128 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest),
2129 None,
2130 ))
2131 .unwrap(),
2132 ),
2133 RpcTest::basic(
2134 EthFeeHistory::request((
2135 10.into(),
2136 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
2137 None,
2138 ))
2139 .unwrap(),
2140 ),
2141 RpcTest::basic(
2142 EthFeeHistory::request((
2143 10.into(),
2144 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
2145 Some(vec![10., 50., 90.]),
2146 ))
2147 .unwrap(),
2148 ),
2149 RpcTest::identity(
2150 EthFeeHistoryV2::request((
2151 10.into(),
2152 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2153 None,
2154 ))
2155 .unwrap(),
2156 ),
2157 RpcTest::identity(
2158 EthFeeHistoryV2::request((
2159 10.into(),
2160 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2161 Some(vec![10., 50., 90.]),
2162 ))
2163 .unwrap(),
2164 ),
2165 RpcTest::identity(
2166 EthFeeHistoryV2::request((
2167 10.into(),
2168 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest),
2169 None,
2170 ))
2171 .unwrap(),
2172 )
2173 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2174 RpcTest::basic(
2175 EthFeeHistoryV2::request((
2176 10.into(),
2177 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending),
2178 Some(vec![10., 50., 90.]),
2179 ))
2180 .unwrap(),
2181 ),
2182 RpcTest::basic(
2183 EthFeeHistoryV2::request((
2184 10.into(),
2185 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
2186 None,
2187 ))
2188 .unwrap(),
2189 ),
2190 RpcTest::basic(
2191 EthFeeHistoryV2::request((
2192 10.into(),
2193 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
2194 None,
2195 ))
2196 .unwrap(),
2197 ),
2198 RpcTest::basic(
2199 EthFeeHistoryV2::request((
2200 10.into(),
2201 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
2202 Some(vec![10., 50., 90.]),
2203 ))
2204 .unwrap(),
2205 ),
2206 RpcTest::identity(
2207 EthGetCode::request((
2208 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2210 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2211 ))
2212 .unwrap(),
2213 ),
2214 RpcTest::identity(
2215 EthGetCode::request((
2216 Address::from_str("f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq")
2218 .unwrap()
2219 .try_into()
2220 .unwrap(),
2221 BlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2222 ))
2223 .unwrap(),
2224 ),
2225 RpcTest::identity(
2226 EthGetCode::request((
2227 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2228 BlockNumberOrHash::from_predefined(Predefined::Earliest),
2229 ))
2230 .unwrap(),
2231 )
2232 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2233 RpcTest::basic(
2234 EthGetCode::request((
2235 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2236 BlockNumberOrHash::from_predefined(Predefined::Pending),
2237 ))
2238 .unwrap(),
2239 ),
2240 RpcTest::basic(
2241 EthGetCode::request((
2242 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2243 BlockNumberOrHash::from_predefined(Predefined::Latest),
2244 ))
2245 .unwrap(),
2246 ),
2247 RpcTest::identity(
2248 EthGetCode::request((
2249 generate_eth_random_address().unwrap(),
2250 BlockNumberOrHash::from_predefined(Predefined::Latest),
2251 ))
2252 .unwrap(),
2253 ),
2254 RpcTest::identity(
2255 EthGetCodeV2::request((
2256 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2258 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2259 ))
2260 .unwrap(),
2261 ),
2262 RpcTest::basic(
2263 EthGetCodeV2::request((
2264 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2265 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),
2266 ))
2267 .unwrap(),
2268 ),
2269 RpcTest::basic(
2270 EthGetCodeV2::request((
2271 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2272 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized),
2273 ))
2274 .unwrap(),
2275 ),
2276 RpcTest::identity(
2277 EthGetCodeV2::request((
2278 generate_eth_random_address().unwrap(),
2279 ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),
2280 ))
2281 .unwrap(),
2282 ),
2283 RpcTest::identity(
2284 EthGetTransactionByBlockNumberAndIndex::request((
2285 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
2286 0.into(),
2287 ))
2288 .unwrap(),
2289 )
2290 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2291 RpcTest::identity(
2292 EthGetTransactionByBlockNumberAndIndex::request((
2293 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest),
2294 0.into(),
2295 ))
2296 .unwrap(),
2297 )
2298 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2299 RpcTest::identity(
2300 EthGetTransactionByBlockNumberAndIndex::request((
2301 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending),
2302 0.into(),
2303 ))
2304 .unwrap(),
2305 )
2306 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2307 RpcTest::identity(
2308 EthGetTransactionByBlockNumberAndIndex::request((
2309 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest),
2310 0.into(),
2311 ))
2312 .unwrap(),
2313 )
2314 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2315 RpcTest::identity(
2316 EthGetTransactionByBlockNumberAndIndexV2::request((
2317 BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()),
2318 0.into(),
2319 ))
2320 .unwrap(),
2321 )
2322 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2323 RpcTest::identity(
2324 EthGetTransactionByBlockNumberAndIndexV2::request((
2325 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe),
2326 0.into(),
2327 ))
2328 .unwrap(),
2329 )
2330 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2331 RpcTest::identity(
2332 EthGetTransactionByBlockNumberAndIndexV2::request((
2333 BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized),
2334 0.into(),
2335 ))
2336 .unwrap(),
2337 )
2338 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2339 RpcTest::identity(
2340 EthGetTransactionByBlockHashAndIndex::request((block_hash.clone(), 0.into())).unwrap(),
2341 )
2342 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
2343 RpcTest::identity(EthGetBlockByHash::request((block_hash.clone(), false)).unwrap()),
2344 RpcTest::identity(EthGetBlockByHash::request((block_hash.clone(), true)).unwrap()),
2345 RpcTest::identity(
2346 EthGetLogs::request((EthFilterSpec {
2347 from_block: Some(format!("0x{:x}", shared_tipset.epoch())),
2348 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
2349 ..Default::default()
2350 },))
2351 .unwrap(),
2352 )
2353 .sort_policy(SortPolicy::All)
2354 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2355 RpcTest::identity(
2356 EthGetLogs::request((EthFilterSpec {
2357 from_block: Some(format!("0x{:x}", shared_tipset.epoch())),
2358 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
2359 address: Some(EthAddressList::List(Vec::new())),
2360 ..Default::default()
2361 },))
2362 .unwrap(),
2363 )
2364 .sort_policy(SortPolicy::All)
2365 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2366 RpcTest::identity(
2367 EthGetLogs::request((EthFilterSpec {
2368 from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)),
2369 to_block: Some(format!("0x{:x}", shared_tipset.epoch())),
2370 ..Default::default()
2371 },))
2372 .unwrap(),
2373 )
2374 .sort_policy(SortPolicy::All)
2375 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2376 RpcTest::identity(
2377 EthGetLogs::request((EthFilterSpec {
2378 address: Some(EthAddressList::Single(
2379 EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(),
2380 )),
2381 ..Default::default()
2382 },))
2383 .unwrap(),
2384 )
2385 .sort_policy(SortPolicy::All)
2386 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2387 RpcTest::identity(EthGetFilterLogs::request((FilterID::new().unwrap(),)).unwrap())
2388 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
2389 RpcTest::identity(EthGetFilterChanges::request((FilterID::new().unwrap(),)).unwrap())
2390 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
2391 RpcTest::identity(EthGetTransactionHashByCid::request((block_cid,)).unwrap()),
2392 RpcTest::identity(
2393 EthTraceBlock::request((ExtBlockNumberOrHash::from_block_number(
2394 shared_tipset.epoch(),
2395 ),))
2396 .unwrap(),
2397 ),
2398 RpcTest::identity(
2399 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
2400 ExtPredefined::Earliest,
2401 ),))
2402 .unwrap(),
2403 )
2404 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2405 RpcTest::basic(
2406 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
2407 ExtPredefined::Pending,
2408 ),))
2409 .unwrap(),
2410 ),
2411 RpcTest::basic(
2412 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),))
2413 .unwrap(),
2414 ),
2415 RpcTest::basic(
2416 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),))
2417 .unwrap(),
2418 ),
2419 RpcTest::basic(
2420 EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(
2421 ExtPredefined::Finalized,
2422 ),))
2423 .unwrap(),
2424 ),
2425 RpcTest::identity(
2426 EthTraceBlockV2::request((ExtBlockNumberOrHash::from_block_number(
2427 shared_tipset.epoch(),
2428 ),))
2429 .unwrap(),
2430 ),
2431 RpcTest::basic(
2432 EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined(
2433 ExtPredefined::Pending,
2434 ),))
2435 .unwrap(),
2436 ),
2437 RpcTest::basic(
2438 EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined(
2439 ExtPredefined::Latest,
2440 ),))
2441 .unwrap(),
2442 ),
2443 RpcTest::basic(
2444 EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),))
2445 .unwrap(),
2446 ),
2447 RpcTest::basic(
2448 EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined(
2449 ExtPredefined::Finalized,
2450 ),))
2451 .unwrap(),
2452 ),
2453 RpcTest::identity(
2454 EthTraceReplayBlockTransactions::request((
2455 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2456 vec!["trace".to_string()],
2457 ))
2458 .unwrap(),
2459 ),
2460 RpcTest::identity(
2461 EthTraceReplayBlockTransactionsV2::request((
2462 ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()),
2463 vec!["trace".to_string()],
2464 ))
2465 .unwrap(),
2466 ),
2467 RpcTest::identity(
2468 EthTraceFilter::request((EthTraceFilterCriteria {
2469 from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)),
2470 to_block: Some(format!("0x{:x}", shared_tipset.epoch() - SAFE_EPOCH_DELAY)),
2471 ..Default::default()
2472 },))
2473 .unwrap(),
2474 )
2475 .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
2478 RpcTest::identity(
2479 EthGetTransactionReceipt::request((
2480 EthHash::from_str(
2483 "0xf234567890123456789d6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f70809",
2484 )
2485 .unwrap(),
2486 ))
2487 .unwrap(),
2488 ),
2489 ];
2490
2491 for block in shared_tipset.block_headers() {
2492 tests.extend([RpcTest::identity(
2493 FilecoinAddressToEthAddress::request((
2494 block.miner_address,
2495 Some(BlockNumberOrPredefined::PredefinedBlock(
2496 ExtPredefined::Latest,
2497 )),
2498 ))
2499 .unwrap(),
2500 )]);
2501 let (bls_messages, secp_messages) =
2502 crate::chain::store::block_messages(store, block).unwrap();
2503 for msg in sample_messages(bls_messages.iter(), secp_messages.iter()) {
2504 tests.extend([RpcTest::identity(
2505 FilecoinAddressToEthAddress::request((
2506 msg.from(),
2507 Some(BlockNumberOrPredefined::PredefinedBlock(
2508 ExtPredefined::Latest,
2509 )),
2510 ))
2511 .unwrap(),
2512 )]);
2513 if let Ok(eth_to_addr) = EthAddress::try_from(msg.to) {
2514 tests.extend([RpcTest::identity(
2515 EthEstimateGas::request((
2516 EthCallMessage {
2517 to: Some(eth_to_addr.clone()),
2518 value: Some(msg.value.clone().into()),
2519 data: Some(msg.params.clone().into()),
2520 ..Default::default()
2521 },
2522 Some(BlockNumberOrHash::BlockNumber(shared_tipset.epoch().into())),
2523 ))
2524 .unwrap(),
2525 )
2526 .policy_on_rejected(PolicyOnRejected::Pass)]);
2527 tests.extend([RpcTest::identity(
2528 EthEstimateGasV2::request((
2529 EthCallMessage {
2530 to: Some(eth_to_addr),
2531 value: Some(msg.value.clone().into()),
2532 data: Some(msg.params.clone().into()),
2533 ..Default::default()
2534 },
2535 Some(ExtBlockNumberOrHash::BlockNumber(
2536 shared_tipset.epoch().into(),
2537 )),
2538 ))
2539 .unwrap(),
2540 )
2541 .policy_on_rejected(PolicyOnRejected::Pass)]);
2542 }
2543 }
2544 }
2545
2546 tests
2547}
2548
2549fn read_state_api_tests(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
2550 let tests = vec![
2551 RpcTest::identity(StateReadState::request((
2552 Address::SYSTEM_ACTOR,
2553 tipset.key().into(),
2554 ))?),
2555 RpcTest::identity(StateReadState::request((
2556 Address::SYSTEM_ACTOR,
2557 Default::default(),
2558 ))?),
2559 RpcTest::identity(StateReadState::request((
2560 Address::CRON_ACTOR,
2561 tipset.key().into(),
2562 ))?),
2563 RpcTest::identity(StateReadState::request((
2564 Address::MARKET_ACTOR,
2565 tipset.key().into(),
2566 ))?),
2567 RpcTest::identity(StateReadState::request((
2568 Address::INIT_ACTOR,
2569 tipset.key().into(),
2570 ))?),
2571 RpcTest::identity(StateReadState::request((
2572 Address::POWER_ACTOR,
2573 tipset.key().into(),
2574 ))?),
2575 RpcTest::identity(StateReadState::request((
2576 Address::REWARD_ACTOR,
2577 tipset.key().into(),
2578 ))?),
2579 RpcTest::identity(StateReadState::request((
2580 Address::VERIFIED_REGISTRY_ACTOR,
2581 tipset.key().into(),
2582 ))?),
2583 RpcTest::identity(StateReadState::request((
2584 Address::DATACAP_TOKEN_ACTOR,
2585 tipset.key().into(),
2586 ))?),
2587 RpcTest::identity(StateReadState::request((
2588 Address::new_id(66116), tipset.key().into(),
2591 ))?),
2592 RpcTest::identity(StateReadState::request((
2593 Address::new_id(18101), tipset.key().into(),
2596 ))?),
2597 RpcTest::identity(StateReadState::request((
2598 ACCOUNT_ADDRESS,
2599 tipset.key().into(),
2600 ))?),
2601 RpcTest::identity(StateReadState::request((
2602 MINER_ADDRESS,
2603 tipset.key().into(),
2604 ))?),
2605 RpcTest::identity(StateReadState::request((
2606 Address::from_str(EVM_ADDRESS).unwrap(), tipset.key().into(),
2608 ))?),
2609 ];
2610
2611 Ok(tests)
2612}
2613
2614fn eth_state_tests_with_tipset<DB: Blockstore>(
2615 store: &Arc<DB>,
2616 shared_tipset: &Tipset,
2617 eth_chain_id: EthChainIdType,
2618) -> anyhow::Result<Vec<RpcTest>> {
2619 let mut tests = vec![];
2620
2621 for block in shared_tipset.block_headers() {
2622 let state = StateTree::new_from_root(store.clone(), shared_tipset.parent_state())?;
2623
2624 let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?;
2625 for smsg in sample_signed_messages(bls_messages.iter(), secp_messages.iter()) {
2626 let tx = new_eth_tx_from_signed_message(&smsg, &state, eth_chain_id)?;
2627 tests.push(RpcTest::identity(
2628 EthGetMessageCidByTransactionHash::request((tx.hash.clone(),))?,
2629 ));
2630 tests.push(RpcTest::identity(EthGetTransactionByHash::request((tx
2631 .hash
2632 .clone(),))?));
2633 tests.push(RpcTest::identity(EthGetTransactionByHashLimited::request(
2634 (tx.hash.clone(), shared_tipset.epoch()),
2635 )?));
2636 tests.push(RpcTest::identity(EthTraceTransaction::request((tx
2637 .hash
2638 .to_string(),))?));
2639 if smsg.message.from.protocol() == Protocol::Delegated
2640 && smsg.message.to.protocol() == Protocol::Delegated
2641 {
2642 tests.push(
2643 RpcTest::identity(EthGetTransactionReceipt::request((tx.hash.clone(),))?)
2644 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2645 );
2646 tests.push(
2647 RpcTest::identity(EthGetTransactionReceiptLimited::request((tx.hash, 800))?)
2648 .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError),
2649 );
2650 }
2651 }
2652 }
2653 tests.push(RpcTest::identity(
2654 EthGetMessageCidByTransactionHash::request((EthHash::from_str(
2655 "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355f",
2656 )?,))?,
2657 ));
2658
2659 tests.extend(eth_call_api_err_tests(shared_tipset.epoch()));
2661
2662 Ok(tests)
2663}
2664
2665fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec<RpcTest> {
2666 let addr = Address::from_str("t15ydyu3d65gznpp2qxwpkjsgz4waubeunn6upvla").unwrap();
2669 let message = Message {
2670 from: addr,
2671 to: addr,
2672 value: TokenAmount::from_whole(1),
2673 method_num: METHOD_SEND,
2674 ..Default::default()
2675 };
2676
2677 vec![
2678 RpcTest::identity(
2684 GasEstimateGasLimit::request((message.clone(), shared_tipset.key().into())).unwrap(),
2685 ),
2686 RpcTest::validate(
2690 GasEstimateMessageGas::request((
2691 message,
2692 None, shared_tipset.key().into(),
2694 ))
2695 .unwrap(),
2696 |forest_api_msg, lotus_api_msg| {
2697 let forest_msg = forest_api_msg.message;
2698 let lotus_msg = lotus_api_msg.message;
2699 if forest_msg.gas_limit != lotus_msg.gas_limit {
2701 return false;
2702 }
2703
2704 let forest_fee_cap = &forest_msg.gas_fee_cap;
2706 let lotus_fee_cap = &lotus_msg.gas_fee_cap;
2707 let forest_premium = &forest_msg.gas_premium;
2708 let lotus_premium = &lotus_msg.gas_premium;
2709
2710 if [forest_fee_cap, lotus_fee_cap, forest_premium, lotus_premium]
2712 .iter()
2713 .any(|amt| amt.is_negative())
2714 {
2715 return false;
2716 }
2717
2718 forest_fee_cap.is_within_percent(lotus_fee_cap, 5)
2719 && forest_premium.is_within_percent(lotus_premium, 5)
2720 },
2721 ),
2722 ]
2723}
2724
2725fn f3_tests() -> anyhow::Result<Vec<RpcTest>> {
2726 Ok(vec![
2727 RpcTest::basic(F3GetECPowerTable::request((None.into(),))?),
2729 RpcTest::basic(F3GetLatestCertificate::request(())?),
2730 RpcTest::basic(F3ListParticipants::request(())?),
2731 RpcTest::basic(F3GetProgress::request(())?),
2732 RpcTest::basic(F3GetOrRenewParticipationTicket::request((
2733 Address::new_id(1000),
2734 vec![],
2735 3,
2736 ))?),
2737 RpcTest::identity(F3IsRunning::request(())?),
2738 RpcTest::identity(F3GetCertificate::request((0,))?),
2739 RpcTest::identity(F3GetCertificate::request((100,))?),
2740 RpcTest::identity(F3GetManifest::request(())?),
2741 ])
2742}
2743
2744fn f3_tests_with_tipset(tipset: &Tipset) -> anyhow::Result<Vec<RpcTest>> {
2745 Ok(vec![
2746 RpcTest::identity(F3GetECPowerTable::request((tipset.key().into(),))?),
2747 RpcTest::identity(F3GetF3PowerTable::request((tipset.key().into(),))?),
2748 ])
2749}
2750
2751fn snapshot_tests(
2754 store: Arc<ManyCar>,
2755 offline: bool,
2756 num_tipsets: usize,
2757 miner_address: Option<Address>,
2758 eth_chain_id: u64,
2759) -> anyhow::Result<Vec<RpcTest>> {
2760 let mut tests = vec![];
2761 let shared_tipset = store
2764 .heaviest_tipset()?
2765 .chain(&store)
2766 .take(SAFE_EPOCH_DELAY as usize)
2767 .last()
2768 .expect("Infallible");
2769
2770 for tipset in shared_tipset.chain(&store).take(num_tipsets) {
2771 tests.extend(chain_tests_with_tipset(&store, offline, &tipset)?);
2772 tests.extend(miner_tests_with_tipset(&store, &tipset, miner_address)?);
2773 tests.extend(state_tests_with_tipset(&store, &tipset)?);
2774 tests.extend(eth_tests_with_tipset(&store, &tipset));
2775 tests.extend(event_tests_with_tipset(&store, &tipset));
2776 tests.extend(gas_tests_with_tipset(&tipset));
2777 tests.extend(mpool_tests_with_tipset(&tipset));
2778 tests.extend(eth_state_tests_with_tipset(&store, &tipset, eth_chain_id)?);
2779 tests.extend(f3_tests_with_tipset(&tipset)?);
2780 }
2781
2782 Ok(tests)
2783}
2784
2785fn sample_message_cids<'a>(
2786 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2787 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2788) -> impl Iterator<Item = Cid> + 'a {
2789 bls_messages
2790 .map(|m| m.cid())
2791 .unique()
2792 .take(COLLECTION_SAMPLE_SIZE)
2793 .chain(
2794 secp_messages
2795 .map(|m| m.cid())
2796 .unique()
2797 .take(COLLECTION_SAMPLE_SIZE),
2798 )
2799 .unique()
2800}
2801
2802fn sample_messages<'a>(
2803 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2804 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2805) -> impl Iterator<Item = &'a Message> + 'a {
2806 bls_messages
2807 .unique()
2808 .take(COLLECTION_SAMPLE_SIZE)
2809 .chain(
2810 secp_messages
2811 .map(SignedMessage::message)
2812 .unique()
2813 .take(COLLECTION_SAMPLE_SIZE),
2814 )
2815 .unique()
2816}
2817
2818fn sample_signed_messages<'a>(
2819 bls_messages: impl Iterator<Item = &'a Message> + 'a,
2820 secp_messages: impl Iterator<Item = &'a SignedMessage> + 'a,
2821) -> impl Iterator<Item = SignedMessage> + 'a {
2822 bls_messages
2823 .unique()
2824 .take(COLLECTION_SAMPLE_SIZE)
2825 .map(|msg| {
2826 let sig = Signature::new_bls(vec![]);
2827 SignedMessage::new_unchecked(msg.clone(), sig)
2828 })
2829 .chain(secp_messages.cloned().unique().take(COLLECTION_SAMPLE_SIZE))
2830 .unique()
2831}
2832
2833pub(super) async fn create_tests(
2834 CreateTestsArgs {
2835 offline,
2836 n_tipsets,
2837 miner_address,
2838 worker_address,
2839 eth_chain_id,
2840 snapshot_files,
2841 }: CreateTestsArgs,
2842) -> anyhow::Result<Vec<RpcTest>> {
2843 let mut tests = vec![];
2844 tests.extend(auth_tests()?);
2845 tests.extend(common_tests());
2846 tests.extend(chain_tests());
2847 tests.extend(mpool_tests());
2848 tests.extend(net_tests());
2849 tests.extend(node_tests());
2850 tests.extend(wallet_tests(worker_address));
2851 tests.extend(eth_tests());
2852 tests.extend(f3_tests()?);
2853 if !snapshot_files.is_empty() {
2854 let store = Arc::new(ManyCar::try_from(snapshot_files.clone())?);
2855 revalidate_chain(store.clone(), n_tipsets).await?;
2856 tests.extend(snapshot_tests(
2857 store,
2858 offline,
2859 n_tipsets,
2860 miner_address,
2861 eth_chain_id,
2862 )?);
2863 }
2864 tests.sort_by_key(|test| test.request.method_name.clone());
2865
2866 tests.extend(create_deferred_tests(snapshot_files)?);
2867 Ok(tests)
2868}
2869
2870fn create_deferred_tests(snapshot_files: Vec<PathBuf>) -> anyhow::Result<Vec<RpcTest>> {
2872 let mut tests = vec![];
2873
2874 if !snapshot_files.is_empty() {
2875 let store = Arc::new(ManyCar::try_from(snapshot_files)?);
2876 tests.push(RpcTest::identity(ChainSetHead::request((store
2877 .heaviest_tipset()?
2878 .key()
2879 .clone(),))?));
2880 }
2881
2882 Ok(tests)
2883}
2884
2885async fn revalidate_chain(db: Arc<ManyCar>, n_ts_to_validate: usize) -> anyhow::Result<()> {
2886 if n_ts_to_validate == 0 {
2887 return Ok(());
2888 }
2889 let chain_config = Arc::new(handle_chain_config(&NetworkChain::Calibnet)?);
2890
2891 let genesis_header = crate::genesis::read_genesis_header(
2892 None,
2893 chain_config.genesis_bytes(&db).await?.as_deref(),
2894 &db,
2895 )
2896 .await?;
2897 let chain_store = Arc::new(ChainStore::new(
2898 db.clone(),
2899 db.clone(),
2900 db.clone(),
2901 chain_config,
2902 genesis_header.clone(),
2903 )?);
2904 let state_manager = Arc::new(StateManager::new(chain_store.clone())?);
2905 let head_ts = db.heaviest_tipset()?;
2906
2907 proofs_api::maybe_set_proofs_parameter_cache_dir_env(&Config::default().client.data_dir);
2910 ensure_proof_params_downloaded().await?;
2911 state_manager.validate_tipsets(
2912 head_ts
2913 .chain(&db)
2914 .take(SAFE_EPOCH_DELAY as usize + n_ts_to_validate),
2915 )?;
2916
2917 Ok(())
2918}
2919
2920#[allow(clippy::too_many_arguments)]
2921pub(super) async fn run_tests(
2922 tests: impl IntoIterator<Item = RpcTest>,
2923 forest: impl Into<Arc<rpc::Client>>,
2924 lotus: impl Into<Arc<rpc::Client>>,
2925 max_concurrent_requests: usize,
2926 filter_file: Option<PathBuf>,
2927 filter: String,
2928 filter_version: Option<rpc::ApiPaths>,
2929 run_ignored: RunIgnored,
2930 fail_fast: bool,
2931 dump_dir: Option<PathBuf>,
2932 test_criteria_overrides: &[TestCriteriaOverride],
2933 report_dir: Option<PathBuf>,
2934 report_mode: ReportMode,
2935) -> anyhow::Result<()> {
2936 let forest = Into::<Arc<rpc::Client>>::into(forest);
2937 let lotus = Into::<Arc<rpc::Client>>::into(lotus);
2938 let semaphore = Arc::new(Semaphore::new(max_concurrent_requests));
2939 let mut futures = FuturesUnordered::new();
2940
2941 let filter_list = if let Some(filter_file) = &filter_file {
2942 FilterList::new_from_file(filter_file)?
2943 } else {
2944 FilterList::default().allow(filter.clone())
2945 };
2946
2947 let mut report_builder = ReportBuilder::new(&filter_list, report_mode);
2949
2950 for test in tests.into_iter().unique_by(
2952 |RpcTest {
2953 request:
2954 rpc::Request {
2955 method_name,
2956 params,
2957 api_paths,
2958 ..
2959 },
2960 ignore,
2961 ..
2962 }| {
2963 (
2964 method_name.clone(),
2965 params.clone(),
2966 *api_paths,
2967 ignore.is_some(),
2968 )
2969 },
2970 ) {
2971 if matches!(run_ignored, RunIgnored::Default) && test.ignore.is_some() {
2973 continue;
2974 }
2975 if matches!(run_ignored, RunIgnored::IgnoredOnly) && test.ignore.is_none() {
2977 continue;
2978 }
2979
2980 if !filter_list.authorize(&test.request.method_name) {
2981 continue;
2982 }
2983
2984 if let Some(filter_version) = filter_version
2985 && !test.request.api_paths.contains(filter_version)
2986 {
2987 continue;
2988 }
2989
2990 let permit = semaphore.clone().acquire_owned().await?;
2992 let forest = forest.clone();
2993 let lotus = lotus.clone();
2994 let future = tokio::spawn(async move {
2995 let test_result = test.run(&forest, &lotus).await;
2996 drop(permit); (test, test_result)
2998 });
2999
3000 futures.push(future);
3001 }
3002
3003 if futures.is_empty() {
3005 return Ok(());
3006 }
3007
3008 while let Some(Ok((test, test_result))) = futures.next().await {
3009 let method_name = test.request.method_name.clone();
3010 let success = evaluate_test_success(&test_result, &test, test_criteria_overrides);
3011
3012 report_builder.track_test_result(
3013 method_name.as_ref(),
3014 success,
3015 &test_result,
3016 &test.request.params,
3017 );
3018
3019 if let (Some(dump_dir), Some(test_dump)) = (&dump_dir, &test_result.test_dump) {
3021 dump_test_data(dump_dir, success, test_dump)?;
3022 }
3023
3024 if !success && fail_fast {
3025 break;
3026 }
3027 }
3028
3029 let has_failures = report_builder.has_failures();
3030 report_builder.print_summary();
3031
3032 if let Some(path) = report_dir {
3033 report_builder.finalize_and_save(&path)?;
3034 }
3035
3036 anyhow::ensure!(!has_failures, "Some tests failed");
3037
3038 Ok(())
3039}
3040
3041fn evaluate_test_success(
3043 test_result: &TestResult,
3044 test: &RpcTest,
3045 test_criteria_overrides: &[TestCriteriaOverride],
3046) -> bool {
3047 match (&test_result.forest_status, &test_result.lotus_status) {
3048 (TestSummary::Valid, TestSummary::Valid) => true,
3049 (TestSummary::Valid, TestSummary::Timeout) => {
3050 test_criteria_overrides.contains(&TestCriteriaOverride::ValidAndTimeout)
3051 }
3052 (TestSummary::Timeout, TestSummary::Timeout) => {
3053 test_criteria_overrides.contains(&TestCriteriaOverride::TimeoutAndTimeout)
3054 }
3055 (TestSummary::Rejected(reason_forest), TestSummary::Rejected(reason_lotus)) => {
3056 match test.policy_on_rejected {
3057 PolicyOnRejected::Pass => true,
3058 PolicyOnRejected::PassWithIdenticalError => reason_forest == reason_lotus,
3059 PolicyOnRejected::PassWithIdenticalErrorCaseInsensitive => {
3060 reason_forest.eq_ignore_ascii_case(reason_lotus)
3061 }
3062 PolicyOnRejected::PassWithQuasiIdenticalError => {
3063 reason_lotus.contains(reason_forest) || reason_forest.contains(reason_lotus)
3064 }
3065 _ => false,
3066 }
3067 }
3068 _ => false,
3069 }
3070}
3071
3072fn normalized_error_message(s: &str) -> Cow<'_, str> {
3073 let lotus_gateway_error_prefix = lazy_regex::regex!(r#"^RPC\serror\s\(-?\d+\):\s*"#);
3075 lotus_gateway_error_prefix.replace(s, "")
3076}
3077
3078fn dump_test_data(dump_dir: &Path, success: bool, test_dump: &TestDump) -> anyhow::Result<()> {
3080 let dir = dump_dir.join(if success { "valid" } else { "invalid" });
3081 if !dir.is_dir() {
3082 std::fs::create_dir_all(&dir)?;
3083 }
3084 let file_name = format!(
3085 "{}_{}.json",
3086 test_dump
3087 .request
3088 .method_name
3089 .as_ref()
3090 .replace(".", "_")
3091 .to_lowercase(),
3092 Utc::now().timestamp_micros()
3093 );
3094 std::fs::write(
3095 dir.join(file_name),
3096 serde_json::to_string_pretty(test_dump)?,
3097 )?;
3098 Ok(())
3099}
3100
3101fn validate_message_lookup(req: rpc::Request<MessageLookup>) -> RpcTest {
3102 RpcTest::validate(req, |mut forest, mut lotus| {
3103 forest.return_dec = Ipld::Null;
3105 lotus.return_dec = Ipld::Null;
3106 forest == lotus
3107 })
3108}
3109
3110fn validate_tagged_tipset_v2(req: rpc::Request<Option<Tipset>>, offline: bool) -> RpcTest {
3111 RpcTest::validate(req, move |forest, lotus| match (forest, lotus) {
3112 (None, None) => true,
3113 (Some(forest), Some(lotus)) => {
3114 if offline {
3115 true
3116 } else {
3117 (forest.epoch() - lotus.epoch()).abs() <= 2
3118 }
3119 }
3120 _ => false,
3121 })
3122}
3123
3124#[cfg(test)]
3125mod tests {
3126 use super::*;
3127
3128 #[test]
3129 fn test_normalized_error_message_1() {
3130 let s = "RPC error (-32603): exactly one tipset selection criteria must be specified";
3131 let r = normalized_error_message(s);
3132 assert_eq!(
3133 r.as_ref(),
3134 "exactly one tipset selection criteria must be specified"
3135 );
3136 }
3137
3138 #[test]
3139 fn test_normalized_error_message_2() {
3140 let s = "exactly one tipset selection criteria must be specified";
3141 let r = normalized_error_message(s);
3142 assert_eq!(
3143 r.as_ref(),
3144 "exactly one tipset selection criteria must be specified"
3145 );
3146 }
3147}