From c4581a16c6e009871a77e84b65e862efc3c2811f Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 09:38:38 -0400 Subject: [PATCH 01/15] State diff on root mismatch option --- Cargo.lock | 25 ++++++- ipld/blockstore/Cargo.toml | 7 +- ipld/blockstore/src/lib.rs | 2 + ipld/blockstore/src/resolve.rs | 50 ++++++++++++++ tests/conformance_tests/Cargo.toml | 5 +- .../tests/conformance_runner.rs | 67 +++++++++++++++---- 6 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 ipld/blockstore/src/resolve.rs diff --git a/Cargo.lock b/Cargo.lock index 3cd4233cfd35..ce8fb048bc03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1087,6 +1087,7 @@ dependencies = [ "base64 0.12.3", "clock", "db", + "difference", "fil_types", "flate2", "forest_address", @@ -1095,6 +1096,7 @@ dependencies = [ "forest_cid", "forest_crypto", "forest_encoding", + "forest_ipld", "forest_message", "forest_vm", "interpreter", @@ -1106,6 +1108,7 @@ dependencies = [ "serde", "serde_json", "state_manager", + "term", "walkdir", ] @@ -1589,6 +1592,16 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + [[package]] name = "dirs" version = "3.0.1" @@ -5923,6 +5936,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "term" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" +dependencies = [ + "dirs 2.0.2", + "winapi 0.3.9", +] + [[package]] name = "termcolor" version = "1.1.0" @@ -6382,7 +6405,7 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" name = "utils" version = "0.1.0" dependencies = [ - "dirs", + "dirs 3.0.1", "serde", "serde_derive", "toml", diff --git a/ipld/blockstore/Cargo.toml b/ipld/blockstore/Cargo.toml index c56744eabc88..ecbb38d8dc5a 100644 --- a/ipld/blockstore/Cargo.toml +++ b/ipld/blockstore/Cargo.toml @@ -8,10 +8,11 @@ edition = "2018" cid = { package = "forest_cid", path = "../cid" } db = { path = "../../node/db" } encoding = { package = "forest_encoding", path = "../../encoding" } -forest_ipld = { path = "../" } +forest_ipld = { path = "../", optional = true } commcid = { path = "../../utils/commcid", optional = true } [features] rocksdb = ["db/rocksdb"] -buffered = ["commcid"] -tracking = [] \ No newline at end of file +buffered = ["commcid", "forest_ipld"] +tracking = [] +resolve = ["forest_ipld"] diff --git a/ipld/blockstore/src/lib.rs b/ipld/blockstore/src/lib.rs index 9904fc16077d..61bc932bec24 100644 --- a/ipld/blockstore/src/lib.rs +++ b/ipld/blockstore/src/lib.rs @@ -5,6 +5,8 @@ mod buffered; #[cfg(feature = "tracking")] mod tracking; +#[cfg(feature = "resolve")] +pub mod resolve; #[cfg(feature = "buffered")] pub use self::buffered::BufferedBlockStore; diff --git a/ipld/blockstore/src/resolve.rs b/ipld/blockstore/src/resolve.rs new file mode 100644 index 000000000000..42eff4a22f76 --- /dev/null +++ b/ipld/blockstore/src/resolve.rs @@ -0,0 +1,50 @@ +use super::BlockStore; +use cid::Cid; +use forest_ipld::Ipld; +use std::error::Error as StdError; + +/// Resolves link to recursively resolved Ipld with no hash links. +pub fn resolve_cids_recursive(bs: &BS, cid: &Cid) -> Result> +where + BS: BlockStore, +{ + let mut ipld = bs + .get(cid)? + .ok_or_else(|| "Cid does not exist in blockstore")?; + + resolve_ipld(bs, &mut ipld)?; + + Ok(ipld) +} + +/// Resolves Ipld links recursively, building an Ipld structure with no hash links. +pub fn resolve_ipld(bs: &BS, ipld: &mut Ipld) -> Result<(), Box> +where + BS: BlockStore, +{ + match ipld { + Ipld::Map(m) => { + for (_, v) in m.iter_mut() { + resolve_ipld(bs, v)?; + } + } + Ipld::List(list) => { + for v in list.iter_mut() { + resolve_ipld(bs, v)?; + } + } + link @ Ipld::Link(_) => { + let resolved: Option = if let Ipld::Link(cid) = link { + bs.get(cid)? + } else { + unreachable!() + }; + + if let Some(ipld) = resolved { + *link = ipld; + } + } + _ => (), + } + Ok(()) +} diff --git a/tests/conformance_tests/Cargo.toml b/tests/conformance_tests/Cargo.toml index ad4de4efa946..2abb48c80e16 100644 --- a/tests/conformance_tests/Cargo.toml +++ b/tests/conformance_tests/Cargo.toml @@ -43,7 +43,7 @@ state_manager = { path = "../../blockchain/state_manager", optional = true } interpreter = { path = "../../vm/interpreter/", optional = true } num-bigint = { path = "../../utils/bigint", package = "forest_bigint", optional = true } vm = { package = "forest_vm", path = "../../vm", optional = true } -blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/", optional = true } +blockstore = { package = "ipld_blockstore", path = "../../ipld/blockstore/", features = ["resolve"], optional = true } crypto = { package = "forest_crypto", path = "../../crypto", optional = true } encoding = { package = "forest_encoding", path = "../../encoding", optional = true } fil_types = { path = "../../types", optional = true } @@ -57,3 +57,6 @@ forest_car = { path = "../../ipld/car" } flate2 = "1.0" lazy_static = "1.4" pretty_env_logger = "0.4.0" +difference = "2.0" +term = "0.6" +ipld = { package = "forest_ipld", path = "../../ipld", features = ["json"] } diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 67ec7b253fd5..403cd23acdf3 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -6,11 +6,15 @@ #[macro_use] extern crate lazy_static; +use blockstore::resolve::resolve_cids_recursive; +use cid::Cid; use conformance_tests::*; +use difference::{Changeset, Difference}; use encoding::Cbor; use flate2::read::GzDecoder; use forest_message::{MessageReceipt, UnsignedMessage}; use interpreter::ApplyRet; +use ipld::json::IpldJsonRef; use regex::Regex; use std::error::Error as StdError; use std::fmt; @@ -174,6 +178,53 @@ fn check_msg_result( Ok(()) } +fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Result<(), String> { + if root != expected_root { + let error_msg = format!( + "wrong post root cid; expected {}, but got {}", + expected_root, root + ); + + if std::env::var("FOREST_DIFF") == Ok("1".to_owned()) { + let expected = + resolve_cids_recursive(bs, &expected_root).expect("Failed to populate Ipld"); + let actual = resolve_cids_recursive(bs, &root).expect("Failed to populate Ipld"); + + let expected_json = serde_json::to_string_pretty(&IpldJsonRef(&expected)).unwrap(); + let actual_json = serde_json::to_string_pretty(&IpldJsonRef(&actual)).unwrap(); + + // Compare both texts, the third parameter defines the split level. + let Changeset { diffs, .. } = Changeset::new(&expected_json, &actual_json, "\n"); + + let mut t = term::stdout().unwrap(); + + writeln!(t, "{}:", error_msg); + + for i in 0..diffs.len() { + match diffs[i] { + Difference::Same(ref x) => { + t.reset().unwrap(); + writeln!(t, " {}", x).unwrap(); + } + Difference::Add(ref x) => { + t.fg(term::color::GREEN).unwrap(); + writeln!(t, "+{}", x).unwrap(); + } + Difference::Rem(ref x) => { + t.fg(term::color::RED).unwrap(); + writeln!(t, "-{}", x).unwrap(); + } + } + } + t.reset().unwrap(); + t.flush().unwrap(); + } + + return Err(error_msg.into()); + } + Ok(()) +} + fn execute_message_vector( selector: Option, car: Vec, @@ -200,13 +251,7 @@ fn execute_message_vector( check_msg_result(receipt, &ret, i)?; } - if root != postconditions.state_tree.root_cid { - return Err(format!( - "wrong post root cid; expected {}, but got {}", - postconditions.state_tree.root_cid, root - ) - .into()); - } + compare_state_roots(&bs, &root, &postconditions.state_tree.root_cid)?; Ok(()) } @@ -255,13 +300,7 @@ fn execute_tipset_vector( root = post_state_root; } - if root != postconditions.state_tree.root_cid { - return Err(format!( - "wrong post root cid; expected {}, but got {}", - postconditions.state_tree.root_cid, root - ) - .into()); - } + compare_state_roots(bs.as_ref(), &root, &postconditions.state_tree.root_cid)?; Ok(()) } From 34a15b536ca626d64b4795d200ff1fa1b4991b95 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 10:52:22 -0400 Subject: [PATCH 02/15] Fix resolution to ignore non hash link Cids --- ipld/blockstore/Cargo.toml | 2 +- ipld/blockstore/src/buffered.rs | 9 +++++++-- ipld/blockstore/src/lib.rs | 4 ++-- ipld/blockstore/src/resolve.rs | 12 +++++++++++- tests/conformance_tests/tests/conformance_runner.rs | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ipld/blockstore/Cargo.toml b/ipld/blockstore/Cargo.toml index ecbb38d8dc5a..58c52ff7f82f 100644 --- a/ipld/blockstore/Cargo.toml +++ b/ipld/blockstore/Cargo.toml @@ -15,4 +15,4 @@ commcid = { path = "../../utils/commcid", optional = true } rocksdb = ["db/rocksdb"] buffered = ["commcid", "forest_ipld"] tracking = [] -resolve = ["forest_ipld"] +resolve = ["forest_ipld", "commcid"] diff --git a/ipld/blockstore/src/buffered.rs b/ipld/blockstore/src/buffered.rs index 093a0b571105..5c6a0a88071b 100644 --- a/ipld/blockstore/src/buffered.rs +++ b/ipld/blockstore/src/buffered.rs @@ -6,7 +6,7 @@ use super::BlockStore; use cid::{ multihash::{Code, MultihashDigest}, - Cid, + Cid, Codec, }; use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; use db::{Error, Store}; @@ -56,7 +56,12 @@ where { // Skip identity and Filecoin commitment Cids let ch = cid.hash.algorithm(); - if ch == Code::Identity || ch == SHA2_256_TRUNC254_PADDED || ch == POSEIDON_BLS12_381_A1_FC1 { + if ch == Code::Identity + || ch == SHA2_256_TRUNC254_PADDED + || ch == POSEIDON_BLS12_381_A1_FC1 + || cid.codec == Codec::FilCommitmentUnsealed + || cid.codec == Codec::FilCommitmentSealed + { return Ok(()); } diff --git a/ipld/blockstore/src/lib.rs b/ipld/blockstore/src/lib.rs index 61bc932bec24..f3eede735a3d 100644 --- a/ipld/blockstore/src/lib.rs +++ b/ipld/blockstore/src/lib.rs @@ -3,10 +3,10 @@ #[cfg(feature = "buffered")] mod buffered; -#[cfg(feature = "tracking")] -mod tracking; #[cfg(feature = "resolve")] pub mod resolve; +#[cfg(feature = "tracking")] +mod tracking; #[cfg(feature = "buffered")] pub use self::buffered::BufferedBlockStore; diff --git a/ipld/blockstore/src/resolve.rs b/ipld/blockstore/src/resolve.rs index 42eff4a22f76..80eeec6f3472 100644 --- a/ipld/blockstore/src/resolve.rs +++ b/ipld/blockstore/src/resolve.rs @@ -1,5 +1,6 @@ use super::BlockStore; -use cid::Cid; +use cid::{multihash::Code, Cid, Codec}; +use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; use forest_ipld::Ipld; use std::error::Error as StdError; @@ -35,6 +36,15 @@ where } link @ Ipld::Link(_) => { let resolved: Option = if let Ipld::Link(cid) = link { + let ch = cid.hash.algorithm(); + if ch == Code::Identity + || ch == SHA2_256_TRUNC254_PADDED + || ch == POSEIDON_BLS12_381_A1_FC1 + || cid.codec == Codec::FilCommitmentUnsealed + || cid.codec == Codec::FilCommitmentSealed + { + return Ok(()); + } bs.get(cid)? } else { unreachable!() diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 403cd23acdf3..d8c1cece6df0 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -198,7 +198,7 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re let mut t = term::stdout().unwrap(); - writeln!(t, "{}:", error_msg); + writeln!(t, "{}:", error_msg).unwrap(); for i in 0..diffs.len() { match diffs[i] { From 956d79b545ea1f5bbb6fa19b6698ab4509f440fe Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 11:07:55 -0400 Subject: [PATCH 03/15] Update colored output dep --- Cargo.lock | 35 +++++++------------ tests/conformance_tests/Cargo.toml | 2 +- .../tests/conformance_runner.rs | 16 +++------ 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce8fb048bc03..183fb0da0a9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1048,6 +1048,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "commcid" version = "0.1.0" @@ -1086,6 +1097,7 @@ dependencies = [ "actor", "base64 0.12.3", "clock", + "colored", "db", "difference", "fil_types", @@ -1108,7 +1120,6 @@ dependencies = [ "serde", "serde_json", "state_manager", - "term", "walkdir", ] @@ -1592,16 +1603,6 @@ dependencies = [ "generic-array 0.14.4", ] -[[package]] -name = "dirs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -dependencies = [ - "cfg-if", - "dirs-sys", -] - [[package]] name = "dirs" version = "3.0.1" @@ -5936,16 +5937,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "term" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" -dependencies = [ - "dirs 2.0.2", - "winapi 0.3.9", -] - [[package]] name = "termcolor" version = "1.1.0" @@ -6405,7 +6396,7 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" name = "utils" version = "0.1.0" dependencies = [ - "dirs 3.0.1", + "dirs", "serde", "serde_derive", "toml", diff --git a/tests/conformance_tests/Cargo.toml b/tests/conformance_tests/Cargo.toml index 2abb48c80e16..5e12ff12e499 100644 --- a/tests/conformance_tests/Cargo.toml +++ b/tests/conformance_tests/Cargo.toml @@ -58,5 +58,5 @@ flate2 = "1.0" lazy_static = "1.4" pretty_env_logger = "0.4.0" difference = "2.0" -term = "0.6" +colored = "2.0" ipld = { package = "forest_ipld", path = "../../ipld", features = ["json"] } diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index d8c1cece6df0..c2a04392d702 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -8,6 +8,7 @@ extern crate lazy_static; use blockstore::resolve::resolve_cids_recursive; use cid::Cid; +use colored::*; use conformance_tests::*; use difference::{Changeset, Difference}; use encoding::Cbor; @@ -196,28 +197,21 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re // Compare both texts, the third parameter defines the split level. let Changeset { diffs, .. } = Changeset::new(&expected_json, &actual_json, "\n"); - let mut t = term::stdout().unwrap(); - - writeln!(t, "{}:", error_msg).unwrap(); + println!("{}:", error_msg); for i in 0..diffs.len() { match diffs[i] { Difference::Same(ref x) => { - t.reset().unwrap(); - writeln!(t, " {}", x).unwrap(); + println!(" {}", x); } Difference::Add(ref x) => { - t.fg(term::color::GREEN).unwrap(); - writeln!(t, "+{}", x).unwrap(); + println!("{}", format!("+{}", x).green()); } Difference::Rem(ref x) => { - t.fg(term::color::RED).unwrap(); - writeln!(t, "-{}", x).unwrap(); + println!("{}", format!("-{}", x).red()); } } } - t.reset().unwrap(); - t.flush().unwrap(); } return Err(error_msg.into()); From 0a19c2e465be7e552432362c88004517223804a9 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 11:21:09 -0400 Subject: [PATCH 04/15] headers --- ipld/blockstore/src/resolve.rs | 3 +++ tests/conformance_tests/tests/conformance_runner.rs | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ipld/blockstore/src/resolve.rs b/ipld/blockstore/src/resolve.rs index 80eeec6f3472..5f4237f6b0cb 100644 --- a/ipld/blockstore/src/resolve.rs +++ b/ipld/blockstore/src/resolve.rs @@ -1,3 +1,6 @@ +// Copyright 2020 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + use super::BlockStore; use cid::{multihash::Code, Cid, Codec}; use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index c2a04392d702..28f8352540fd 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -194,13 +194,12 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re let expected_json = serde_json::to_string_pretty(&IpldJsonRef(&expected)).unwrap(); let actual_json = serde_json::to_string_pretty(&IpldJsonRef(&actual)).unwrap(); - // Compare both texts, the third parameter defines the split level. let Changeset { diffs, .. } = Changeset::new(&expected_json, &actual_json, "\n"); println!("{}:", error_msg); - for i in 0..diffs.len() { - match diffs[i] { + for diff in diffs.into_iter() { + match diff { Difference::Same(ref x) => { println!(" {}", x); } From 00abc001d7db09f2c8f745f6e9e9ebb26e1e850a Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 11:37:37 -0400 Subject: [PATCH 05/15] Update cid codec checks --- ipld/blockstore/Cargo.toml | 8 +++++--- ipld/blockstore/src/buffered.rs | 15 +++------------ ipld/blockstore/src/resolve.rs | 11 ++--------- .../conformance_tests/tests/conformance_runner.rs | 8 ++++---- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/ipld/blockstore/Cargo.toml b/ipld/blockstore/Cargo.toml index 58c52ff7f82f..bddfcf50ed14 100644 --- a/ipld/blockstore/Cargo.toml +++ b/ipld/blockstore/Cargo.toml @@ -9,10 +9,12 @@ cid = { package = "forest_cid", path = "../cid" } db = { path = "../../node/db" } encoding = { package = "forest_encoding", path = "../../encoding" } forest_ipld = { path = "../", optional = true } -commcid = { path = "../../utils/commcid", optional = true } + +[dev-dependencies] +commcid = { path = "../../utils/commcid" } [features] rocksdb = ["db/rocksdb"] -buffered = ["commcid", "forest_ipld"] +buffered = ["forest_ipld"] tracking = [] -resolve = ["forest_ipld", "commcid"] +resolve = ["forest_ipld"] diff --git a/ipld/blockstore/src/buffered.rs b/ipld/blockstore/src/buffered.rs index 5c6a0a88071b..58b6ca947178 100644 --- a/ipld/blockstore/src/buffered.rs +++ b/ipld/blockstore/src/buffered.rs @@ -4,11 +4,7 @@ #![cfg(feature = "buffered")] use super::BlockStore; -use cid::{ - multihash::{Code, MultihashDigest}, - Cid, Codec, -}; -use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; +use cid::{multihash::MultihashDigest, Cid, Codec}; use db::{Error, Store}; use encoding::{from_slice, ser::Serialize, to_vec}; use forest_ipld::Ipld; @@ -55,13 +51,7 @@ where BS: BlockStore, { // Skip identity and Filecoin commitment Cids - let ch = cid.hash.algorithm(); - if ch == Code::Identity - || ch == SHA2_256_TRUNC254_PADDED - || ch == POSEIDON_BLS12_381_A1_FC1 - || cid.codec == Codec::FilCommitmentUnsealed - || cid.codec == Codec::FilCommitmentSealed - { + if cid.codec != Codec::DagCBOR { return Ok(()); } @@ -191,6 +181,7 @@ mod tests { use cid::multihash::{Blake2b256, Identity}; use cid::Codec; use commcid::commitment_to_cid; + use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; use forest_ipld::{ipld, Ipld}; #[test] diff --git a/ipld/blockstore/src/resolve.rs b/ipld/blockstore/src/resolve.rs index 5f4237f6b0cb..0a9993fb0c80 100644 --- a/ipld/blockstore/src/resolve.rs +++ b/ipld/blockstore/src/resolve.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::BlockStore; -use cid::{multihash::Code, Cid, Codec}; -use commcid::{POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED}; +use cid::{Cid, Codec}; use forest_ipld::Ipld; use std::error::Error as StdError; @@ -39,13 +38,7 @@ where } link @ Ipld::Link(_) => { let resolved: Option = if let Ipld::Link(cid) = link { - let ch = cid.hash.algorithm(); - if ch == Code::Identity - || ch == SHA2_256_TRUNC254_PADDED - || ch == POSEIDON_BLS12_381_A1_FC1 - || cid.codec == Codec::FilCommitmentUnsealed - || cid.codec == Codec::FilCommitmentSealed - { + if cid.codec != Codec::DagCBOR { return Ok(()); } bs.get(cid)? diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 28f8352540fd..e6af2ffb9772 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -198,15 +198,15 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re println!("{}:", error_msg); - for diff in diffs.into_iter() { + for diff in diffs.iter() { match diff { - Difference::Same(ref x) => { + Difference::Same(x) => { println!(" {}", x); } - Difference::Add(ref x) => { + Difference::Add(x) => { println!("{}", format!("+{}", x).green()); } - Difference::Rem(ref x) => { + Difference::Rem(x) => { println!("{}", format!("-{}", x).red()); } } From 66d87de903dc36586418f499947336abb42dbbdf Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 12:25:12 -0400 Subject: [PATCH 06/15] Try to resolve actor states before defaulting to base Ipld resolve --- Cargo.lock | 2 + tests/conformance_tests/Cargo.toml | 2 + .../tests/conformance_runner.rs | 80 ++++++++++++++++--- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 183fb0da0a9a..dea811e22a84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,7 +1113,9 @@ dependencies = [ "forest_vm", "interpreter", "ipld_blockstore", + "ipld_hamt", "lazy_static", + "log", "pretty_env_logger", "regex", "runtime", diff --git a/tests/conformance_tests/Cargo.toml b/tests/conformance_tests/Cargo.toml index 5e12ff12e499..6bf7dea9f7a0 100644 --- a/tests/conformance_tests/Cargo.toml +++ b/tests/conformance_tests/Cargo.toml @@ -60,3 +60,5 @@ pretty_env_logger = "0.4.0" difference = "2.0" colored = "2.0" ipld = { package = "forest_ipld", path = "../../ipld", features = ["json"] } +ipld_hamt = { path = "../../ipld/hamt" } +log = "0.4" diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index e6af2ffb9772..2b6b5c62cfa5 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -6,22 +6,28 @@ #[macro_use] extern crate lazy_static; +use address::Address; use blockstore::resolve::resolve_cids_recursive; -use cid::Cid; +use cid::{json::CidJson, Cid}; use colored::*; use conformance_tests::*; use difference::{Changeset, Difference}; use encoding::Cbor; +use fil_types::HAMT_BIT_WIDTH; use flate2::read::GzDecoder; use forest_message::{MessageReceipt, UnsignedMessage}; use interpreter::ApplyRet; -use ipld::json::IpldJsonRef; +use ipld::json::{IpldJson, IpldJsonRef}; +use ipld_hamt::{BytesKey, Hamt}; use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; use std::error::Error as StdError; use std::fmt; use std::fs::File; use std::io::BufReader; use std::sync::Arc; +use vm::ActorState; use walkdir::{DirEntry, WalkDir}; lazy_static! { @@ -179,6 +185,54 @@ fn check_msg_result( Ok(()) } +#[derive(Serialize, Deserialize)] +struct ActorStateResolved { + code: CidJson, + sequence: u64, + balance: String, + state: IpldJson, +} + +fn root_to_state_map( + bs: &db::MemoryDB, + root: &Cid, +) -> Result, Box> { + let mut actors = BTreeMap::new(); + let hamt: Hamt<_, _> = Hamt::load_with_bit_width(root, bs, HAMT_BIT_WIDTH)?; + hamt.for_each(|k: &BytesKey, actor: &ActorState| { + let addr = Address::from_bytes(&k.0)?; + + let resolved = resolve_cids_recursive(bs, &actor.state)?; + let resolved_state = ActorStateResolved { + state: IpldJson(resolved), + code: CidJson(actor.code.clone()), + balance: actor.balance.to_string(), + sequence: actor.sequence, + }; + + actors.insert(addr.to_string(), resolved_state); + Ok(()) + })?; + + Ok(actors) +} + +/// Tries to resolve state tree actors, if all data exists in store. +/// The actors hamt is hard to parse in a diff, so this attempts to remedy this. +fn try_resolve_actor_states( + bs: &db::MemoryDB, + root: &Cid, + expected_root: &Cid, +) -> Result> { + let e_state = root_to_state_map(bs, expected_root)?; + let c_state = root_to_state_map(bs, root)?; + + let expected_json = serde_json::to_string_pretty(&e_state)?; + let actual_json = serde_json::to_string_pretty(&c_state)?; + + Ok(Changeset::new(&expected_json, &actual_json, "\n")) +} + fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Result<(), String> { if root != expected_root { let error_msg = format!( @@ -187,14 +241,20 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re ); if std::env::var("FOREST_DIFF") == Ok("1".to_owned()) { - let expected = - resolve_cids_recursive(bs, &expected_root).expect("Failed to populate Ipld"); - let actual = resolve_cids_recursive(bs, &root).expect("Failed to populate Ipld"); - - let expected_json = serde_json::to_string_pretty(&IpldJsonRef(&expected)).unwrap(); - let actual_json = serde_json::to_string_pretty(&IpldJsonRef(&actual)).unwrap(); - - let Changeset { diffs, .. } = Changeset::new(&expected_json, &actual_json, "\n"); + let Changeset { diffs, .. } = try_resolve_actor_states(bs, root, expected_root) + .unwrap_or_else(|e| { + log::warn!("Could not resolve actor states: {}", e); + let expected = resolve_cids_recursive(bs, &expected_root) + .expect("Failed to populate Ipld"); + let actual = + resolve_cids_recursive(bs, &root).expect("Failed to populate Ipld"); + + let expected_json = + serde_json::to_string_pretty(&IpldJsonRef(&expected)).unwrap(); + let actual_json = serde_json::to_string_pretty(&IpldJsonRef(&actual)).unwrap(); + + Changeset::new(&expected_json, &actual_json, "\n") + }); println!("{}:", error_msg); From 7040df394ea9a5f324a009d763a91835a7a94748 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 12:50:29 -0400 Subject: [PATCH 07/15] Update error message --- tests/conformance_tests/tests/conformance_runner.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 2b6b5c62cfa5..2713c64e6ee9 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -243,7 +243,10 @@ fn compare_state_roots(bs: &db::MemoryDB, root: &Cid, expected_root: &Cid) -> Re if std::env::var("FOREST_DIFF") == Ok("1".to_owned()) { let Changeset { diffs, .. } = try_resolve_actor_states(bs, root, expected_root) .unwrap_or_else(|e| { - log::warn!("Could not resolve actor states: {}", e); + println!( + "Could not resolve actor states: {}\nUsing default resolution:", + e + ); let expected = resolve_cids_recursive(bs, &expected_root) .expect("Failed to populate Ipld"); let actual = From 496d3af137964ffae27462c3707c5cb9a0dca99c Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 13:16:59 -0400 Subject: [PATCH 08/15] Support extracted vectors by ignoring links that don't exist in store --- ipld/hamt/Cargo.toml | 2 ++ ipld/hamt/src/node.rs | 25 +++++++++++++------ tests/conformance_tests/Cargo.toml | 2 +- .../tests/conformance_runner.rs | 7 ++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/ipld/hamt/Cargo.toml b/ipld/hamt/Cargo.toml index b492669a0ecb..65dc0d818927 100644 --- a/ipld/hamt/Cargo.toml +++ b/ipld/hamt/Cargo.toml @@ -20,6 +20,8 @@ lazycell = "1.2.1" [features] identity = [] go-interop = [] +# This feature should just be used for testing (ignoring links that don't exist in store) +ignore-dead-links = [] [dev-dependencies] hex = "0.4.2" diff --git a/ipld/hamt/src/node.rs b/ipld/hamt/src/node.rs index 72226dce7187..2cd7de0771a2 100644 --- a/ipld/hamt/src/node.rs +++ b/ipld/hamt/src/node.rs @@ -132,9 +132,15 @@ where if let Some(cached_node) = cache.borrow() { cached_node.for_each(store, f)? } else { - let node = store - .get(cid)? - .ok_or_else(|| format!("Node with cid {} not found", cid))?; + let node = if let Some(node) = store.get(cid)? { + node + } else { + #[cfg(not(feature = "ignore-dead-links"))] + return Err(Error::CidNotFound(cid)); + + #[cfg(feature = "ignore-dead-links")] + continue; + }; // Ignore error intentionally, the cache value will always be the same let _ = cache.fill(node); @@ -194,10 +200,15 @@ where // Link node is cached cached_node.get_value(hashed_key, bit_width, depth + 1, key, store) } else { - // Link is not cached, need to load and fill cache, then traverse for value. - let node = store - .get::>>(cid)? - .ok_or_else(|| Error::CidNotFound(cid.to_string()))?; + let node: Box> = if let Some(node) = store.get(cid)? { + node + } else { + #[cfg(not(feature = "ignore-dead-links"))] + return Err(Error::CidNotFound(cid.to_string())); + + #[cfg(feature = "ignore-dead-links")] + return Ok(None); + }; // Intentionally ignoring error, cache will always be the same. let _ = cache.fill(node); diff --git a/tests/conformance_tests/Cargo.toml b/tests/conformance_tests/Cargo.toml index 6bf7dea9f7a0..e0df43c5ae40 100644 --- a/tests/conformance_tests/Cargo.toml +++ b/tests/conformance_tests/Cargo.toml @@ -60,5 +60,5 @@ pretty_env_logger = "0.4.0" difference = "2.0" colored = "2.0" ipld = { package = "forest_ipld", path = "../../ipld", features = ["json"] } -ipld_hamt = { path = "../../ipld/hamt" } +ipld_hamt = { path = "../../ipld/hamt", features = ["ignore-dead-links"] } log = "0.4" diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 2713c64e6ee9..1e5221c05da5 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -18,6 +18,7 @@ use flate2::read::GzDecoder; use forest_message::{MessageReceipt, UnsignedMessage}; use interpreter::ApplyRet; use ipld::json::{IpldJson, IpldJsonRef}; +use ipld::Ipld; use ipld_hamt::{BytesKey, Hamt}; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -202,7 +203,8 @@ fn root_to_state_map( hamt.for_each(|k: &BytesKey, actor: &ActorState| { let addr = Address::from_bytes(&k.0)?; - let resolved = resolve_cids_recursive(bs, &actor.state)?; + let resolved = + resolve_cids_recursive(bs, &actor.state).unwrap_or(Ipld::Link(actor.state.clone())); let resolved_state = ActorStateResolved { state: IpldJson(resolved), code: CidJson(actor.code.clone()), @@ -212,7 +214,8 @@ fn root_to_state_map( actors.insert(addr.to_string(), resolved_state); Ok(()) - })?; + }) + .unwrap(); Ok(actors) } From 489a1280169f3f4669e0eea266df8348252b1133 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 15:03:21 -0400 Subject: [PATCH 09/15] remove unnecessary skip --- tests/conformance_tests/tests/conformance_runner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 1e5221c05da5..429ee4559087 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -113,7 +113,6 @@ lazy_static! { Regex::new(r"test-vectors/corpus/extracted/0001-initial-extraction/fil_1_account/Send/Ok/ext-0001-fil_1_account-Send-Ok-3").unwrap(), Regex::new(r"test-vectors/corpus/extracted/0004-coverage-boost/fil_1_storageminer/Send/SysErrOutOfGas/extracted-msg-fil_1_storageminer-Send-SysErrOutOfGas-*").unwrap(), Regex::new(r"test-vectors/corpus/extracted/0004-coverage-boost/fil_1_storageminer/DeclareFaults/Ok/extracted-msg-fil_1_storageminer-DeclareFaults-Ok-*").unwrap(), - Regex::new(r"test-vectors/corpus/reward/penalties--not-penalized-insufficient-balance-to-cover-gas-and-transfer.json").unwrap(), ]; } From a0f09605807adf7021d8bb2a81e4b346d00febae Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 16:58:12 -0400 Subject: [PATCH 10/15] Fix incorrect test case --- ipld/blockstore/src/buffered.rs | 2 +- tests/conformance_tests/test-vectors | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipld/blockstore/src/buffered.rs b/ipld/blockstore/src/buffered.rs index 58b6ca947178..3e1a2bbb895c 100644 --- a/ipld/blockstore/src/buffered.rs +++ b/ipld/blockstore/src/buffered.rs @@ -206,7 +206,7 @@ mod tests { let str_val = "value"; let value = 8u8; let arr_cid = buf_store.put(&(str_val, value), Blake2b256).unwrap(); - let identity_cid = buf_store.put(&0u8, Identity).unwrap(); + let identity_cid = Cid::new_v1(Codec::Raw, Identity::digest(&[0u8])); // Create map to insert into store let sealed_comm_cid = commitment_to_cid( diff --git a/tests/conformance_tests/test-vectors b/tests/conformance_tests/test-vectors index 27e735da77a5..75f517e3666c 160000 --- a/tests/conformance_tests/test-vectors +++ b/tests/conformance_tests/test-vectors @@ -1 +1 @@ -Subproject commit 27e735da77a5cb4530254d9aff9b866dc782f435 +Subproject commit 75f517e3666cd6f10430de62430eb5354cb6276a From 93a8da6d8313460af177afdf2bc87db99c83e878 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 17:27:35 -0400 Subject: [PATCH 11/15] Fix error type on feature --- ipld/hamt/src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipld/hamt/src/node.rs b/ipld/hamt/src/node.rs index 2cd7de0771a2..eece441f6739 100644 --- a/ipld/hamt/src/node.rs +++ b/ipld/hamt/src/node.rs @@ -136,7 +136,7 @@ where node } else { #[cfg(not(feature = "ignore-dead-links"))] - return Err(Error::CidNotFound(cid)); + return Err(Error::CidNotFound(cid.to_string())); #[cfg(feature = "ignore-dead-links")] continue; From 0be224e71d5b7f0e8df1467b094d8c4e98280e6c Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 18:08:21 -0400 Subject: [PATCH 12/15] trigger CI From f3301d7d5b90632b911f6588e1945d3221487950 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 18:15:47 -0400 Subject: [PATCH 13/15] basefee conversion from float for now --- tests/conformance_tests/src/lib.rs | 3 +-- tests/conformance_tests/src/message.rs | 11 ++--------- tests/conformance_tests/src/tipset.rs | 5 +++-- tests/conformance_tests/tests/conformance_runner.rs | 10 +++++++--- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/tests/conformance_tests/src/lib.rs b/tests/conformance_tests/src/lib.rs index 257e7a561cc2..49aecc99c376 100644 --- a/tests/conformance_tests/src/lib.rs +++ b/tests/conformance_tests/src/lib.rs @@ -20,7 +20,6 @@ use encoding::Cbor; use fil_types::{SealVerifyInfo, WindowPoStVerifyInfo}; use forest_message::{ChainMessage, Message, MessageReceipt, SignedMessage, UnsignedMessage}; use interpreter::{ApplyRet, BlockMessages, Rand, VM}; -use num_bigint::BigInt; use runtime::{ConsensusFault, Syscalls}; use serde::{Deserialize, Deserializer}; use std::error::Error as StdError; @@ -112,7 +111,7 @@ pub struct PreConditions { pub epoch: ChainEpoch, pub state_tree: StateTreeVector, #[serde(default)] - pub basefee: Option, + pub basefee: Option, } #[derive(Debug, Deserialize)] diff --git a/tests/conformance_tests/src/message.rs b/tests/conformance_tests/src/message.rs index daf7288693ea..d85ecd8cc0f3 100644 --- a/tests/conformance_tests/src/message.rs +++ b/tests/conformance_tests/src/message.rs @@ -17,17 +17,10 @@ pub fn execute_message( msg: &ChainMessage, pre_root: &Cid, epoch: ChainEpoch, - basefee: u64, + basefee: TokenAmount, selector: &Option, ) -> Result<(ApplyRet, Cid), Box> { - let mut vm = VM::<_, _, _>::new( - pre_root, - bs, - epoch, - TestSyscalls, - &TestRand, - TokenAmount::from(basefee), - )?; + let mut vm = VM::<_, _, _>::new(pre_root, bs, epoch, TestSyscalls, &TestRand, basefee)?; if let Some(s) = &selector { if s.chaos_actor diff --git a/tests/conformance_tests/src/tipset.rs b/tests/conformance_tests/src/tipset.rs index 9faacea569c2..4bf74478834d 100644 --- a/tests/conformance_tests/src/tipset.rs +++ b/tests/conformance_tests/src/tipset.rs @@ -3,6 +3,7 @@ use super::*; use fil_types::verifier::MockVerifier; +use num_bigint::ToBigInt; use state_manager::StateManager; use std::sync::Arc; @@ -56,7 +57,7 @@ mod block_messages_json { #[derive(Debug, Deserialize)] pub struct TipsetVector { pub epoch: ChainEpoch, - pub basefee: u64, + pub basefee: f64, #[serde(with = "block_messages_json")] pub blocks: Vec, } @@ -83,7 +84,7 @@ pub fn execute_tipset( &tipset.blocks, tipset.epoch, &TestRand, - BigInt::from(tipset.basefee), + tipset.basefee.to_bigint().unwrap_or_default(), Some(|_, msg: &ChainMessage, ret| { _applied_messages.push(msg.clone()); applied_results.push(ret); diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index 2e5f0a21206b..e4a29fa90122 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -20,6 +20,7 @@ use interpreter::ApplyRet; use ipld::json::{IpldJson, IpldJsonRef}; use ipld::Ipld; use ipld_hamt::{BytesKey, Hamt}; +use num_bigint::{BigInt, ToBigInt}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -31,9 +32,8 @@ use std::sync::Arc; use vm::ActorState; use walkdir::{DirEntry, WalkDir}; -const DEFAULT_BASE_FEE: u64 = 100; - lazy_static! { + static ref DEFAULT_BASE_FEE: BigInt = 100.to_bigint().unwrap(); static ref SKIP_TESTS: Vec = vec![ Regex::new(r"test-vectors/corpus/vm_violations/x--").unwrap(), Regex::new(r"test-vectors/corpus/nested/x--").unwrap(), @@ -59,6 +59,7 @@ lazy_static! { Regex::new(r"fil_1_storageminer-DeclareFaultsRecovered-Ok-").unwrap(), Regex::new(r"fil_1_storageminer-PreCommitSector-").unwrap(), Regex::new(r"fil_1_storageminer-ProveCommitSector-SysErrOutOfGas-").unwrap(), + Regex::new(r"fil_1_storageminer-AddLockedFund-19").unwrap(), Regex::new(r"fil_1_storageminer-AddLockedFund-Ok-").unwrap(), Regex::new(r"fil_1_storageminer-SubmitWindowedPoSt-SysErrSenderInvalid-").unwrap(), Regex::new(r"fil_1_storageminer-WithdrawBalance-Ok-").unwrap(), @@ -271,7 +272,10 @@ fn execute_message_vector( &to_chain_msg(msg), &root, epoch, - preconditions.basefee.unwrap_or(DEFAULT_BASE_FEE), + preconditions + .basefee + .map(|i| i.to_bigint().unwrap_or_default()) + .unwrap_or(DEFAULT_BASE_FEE.clone()), &selector, )?; root = post_root; From 37c117628f33fcca4524c57901dc96bcf4e82c8b Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 9 Oct 2020 19:05:00 -0400 Subject: [PATCH 14/15] Change to unwrap --- tests/conformance_tests/tests/conformance_runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conformance_tests/tests/conformance_runner.rs b/tests/conformance_tests/tests/conformance_runner.rs index e4a29fa90122..3133669c123f 100644 --- a/tests/conformance_tests/tests/conformance_runner.rs +++ b/tests/conformance_tests/tests/conformance_runner.rs @@ -274,7 +274,7 @@ fn execute_message_vector( epoch, preconditions .basefee - .map(|i| i.to_bigint().unwrap_or_default()) + .map(|i| i.to_bigint().unwrap()) .unwrap_or(DEFAULT_BASE_FEE.clone()), &selector, )?; From 6b93ea3d8ea15f366e5472914e257720afd81dbc Mon Sep 17 00:00:00 2001 From: austinabell Date: Tue, 13 Oct 2020 10:34:46 -0400 Subject: [PATCH 15/15] clean up resolve logic --- ipld/blockstore/src/resolve.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/ipld/blockstore/src/resolve.rs b/ipld/blockstore/src/resolve.rs index 0a9993fb0c80..941e4904a12c 100644 --- a/ipld/blockstore/src/resolve.rs +++ b/ipld/blockstore/src/resolve.rs @@ -36,18 +36,11 @@ where resolve_ipld(bs, v)?; } } - link @ Ipld::Link(_) => { - let resolved: Option = if let Ipld::Link(cid) = link { - if cid.codec != Codec::DagCBOR { - return Ok(()); + Ipld::Link(cid) => { + if cid.codec == Codec::DagCBOR { + if let Some(x) = bs.get(cid)? { + *ipld = x; } - bs.get(cid)? - } else { - unreachable!() - }; - - if let Some(ipld) = resolved { - *link = ipld; } } _ => (),