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