diff --git a/cgo/fvm.go b/cgo/fvm.go index e9bc543c..e274520a 100644 --- a/cgo/fvm.go +++ b/cgo/fvm.go @@ -90,3 +90,10 @@ func FvmMachineFlush(executor *FvmMachine) ([]byte, error) { } return resp.value.copy(), nil } + +func FvmMachineDumpCache(executor *FvmMachine, blockstoreId uint64) error { + resp := C.fvm_machine_dump_cache(executor, C.uint64_t(blockstoreId)) + defer resp.destroy() + + return CheckErr(resp) +} diff --git a/fvm.go b/fvm.go index 81bbb252..60aa91f6 100644 --- a/fvm.go +++ b/fvm.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/network" + "github.com/ipfs/boxo/blockstore" "github.com/ipfs/go-cid" "golang.org/x/xerrors" ) @@ -187,6 +188,14 @@ func (f *FVM) Flush() (cid.Cid, error) { return cid.Cast(stateRoot) } +// DumpCache dumps the intermediate blockstore cache of the FVM to the provided blockstore. +func (f *FVM) DumpCache(bs blockstore.Blockstore) error { + defer runtime.KeepAlive(f) + bsHandle := cgo.Register(context.TODO(), blockStoreAsExterns{Blockstore: bs}) + defer cgo.Unregister(bsHandle) + return cgo.FvmMachineDumpCache(f.executor, bsHandle) +} + type ApplyRet struct { Return []byte ExitCode uint64 @@ -238,3 +247,32 @@ func reformBigInt(hi, lo uint64) big.Int { int.SetBits(words) return big.NewFromGo(int) } + +// blockStoreAsExterns is a wrapper around a blockstore.Blockstore that implements the cgo.Externs. +// It is only intended for use strictly as a Blockstore, and does not support any of the other +// Externs methods. +type blockStoreAsExterns struct { + blockstore.Blockstore +} + +var _ cgo.Externs = blockStoreAsExterns{} + +func (b blockStoreAsExterns) GetChainRandomness(ctx context.Context, epoch abi.ChainEpoch) ([32]byte, error) { + return [32]byte{}, xerrors.Errorf("GetChainRandomness not supported") +} + +func (b blockStoreAsExterns) GetBeaconRandomness(ctx context.Context, epoch abi.ChainEpoch) ([32]byte, error) { + return [32]byte{}, xerrors.Errorf("GetBeaconRandomness not supported") +} + +func (b blockStoreAsExterns) VerifyConsensusFault(ctx context.Context, h1, h2, extra []byte) (*cgo.ConsensusFault, int64) { + panic("VerifyConsensusFault not supported") +} + +func (b blockStoreAsExterns) TipsetCid(ctx context.Context, epoch abi.ChainEpoch) (cid.Cid, error) { + return cid.Undef, xerrors.Errorf("TipsetCid not supported") +} + +func (b blockStoreAsExterns) View(ctx context.Context, cid cid.Cid, callback func([]byte) error) error { + return xerrors.Errorf("View not supported") +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 096e2b52..5e3a1115 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1411,10 +1411,10 @@ dependencies = [ "derive_more", "filecoin-proofs-api", "fvm-wasm-instrument 0.2.0", - "fvm_ipld_amt", + "fvm_ipld_amt 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "fvm_ipld_hamt", + "fvm_ipld_hamt 0.10.2", "fvm_shared 2.10.0", "lazy_static", "log", @@ -1447,10 +1447,10 @@ dependencies = [ "derive_more", "filecoin-proofs-api", "fvm-wasm-instrument 0.4.0", - "fvm_ipld_amt", + "fvm_ipld_amt 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "fvm_ipld_hamt", + "fvm_ipld_hamt 0.10.2", "fvm_shared 3.12.0", "lazy_static", "log", @@ -1474,8 +1474,6 @@ dependencies = [ [[package]] name = "fvm" version = "4.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d590e1f94d84ffa49eb7d7d3ea8721af1d90e4b793af89095b9e8b0b816f0360" dependencies = [ "ambassador", "anyhow", @@ -1483,10 +1481,10 @@ dependencies = [ "derive_more", "filecoin-proofs-api", "fvm-wasm-instrument 0.4.0", - "fvm_ipld_amt", + "fvm_ipld_amt 0.7.3", "fvm_ipld_blockstore", "fvm_ipld_encoding", - "fvm_ipld_hamt", + "fvm_ipld_hamt 0.10.3", "fvm_shared 4.5.3", "lazy_static", "log", @@ -1527,6 +1525,21 @@ dependencies = [ "wasmprinter 0.2.80", ] +[[package]] +name = "fvm_ipld_amt" +version = "0.7.3" +dependencies = [ + "anyhow", + "cid", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "itertools 0.13.0", + "multihash-codetable", + "once_cell", + "serde", + "thiserror", +] + [[package]] name = "fvm_ipld_amt" version = "0.7.3" @@ -1547,8 +1560,6 @@ dependencies = [ [[package]] name = "fvm_ipld_blockstore" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b8b31e022f71b73440054f7e5171231a1ebc745adf075014d5aa8ea78ea283" dependencies = [ "anyhow", "cid", @@ -1558,8 +1569,6 @@ dependencies = [ [[package]] name = "fvm_ipld_encoding" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723294d1574e0126a9e16069ef6581a2ee3c06eb7d6edc33eb2a976057747bfb" dependencies = [ "anyhow", "cid", @@ -1592,6 +1601,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fvm_ipld_hamt" +version = "0.10.3" +dependencies = [ + "anyhow", + "byteorder", + "cid", + "forest_hash_utils", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "ipld-core", + "multihash-codetable", + "once_cell", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "fvm_shared" version = "2.10.0" @@ -1655,8 +1682,6 @@ dependencies = [ [[package]] name = "fvm_shared" version = "4.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae590927cdaefbf803c43952885cf172e4d8159464f1c82cad8dce22dc09b7b" dependencies = [ "anyhow", "bitflags 2.6.0", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 19159e55..781df4d1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -50,6 +50,12 @@ safer-ffi = { version = "0.1.13", features = ["proc_macros"] } filecoin-proofs-api = { version = "18.1", default-features = false } yastl = "0.1.2" +[patch.crates-io] +fvm4 = { package = "fvm", path = "/home/rvagg/git/pl/filecoin-project/ref-fvm/fvm" } +fvm4_shared = { package = "fvm_shared", path = "/home/rvagg/git/pl/filecoin-project/ref-fvm/shared" } +fvm_ipld_blockstore = { path = "/home/rvagg/git/pl/filecoin-project/ref-fvm/ipld/blockstore" } +fvm_ipld_encoding = { path = "/home/rvagg/git/pl/filecoin-project/ref-fvm/ipld/encoding" } + [dev-dependencies] memmap2 = "0.9" tempfile = "3.12.0" diff --git a/rust/src/fvm/engine.rs b/rust/src/fvm/engine.rs index 985f140b..19607565 100644 --- a/rust/src/fvm/engine.rs +++ b/rust/src/fvm/engine.rs @@ -16,7 +16,7 @@ use super::blockstore::CgoBlockstore; use super::externs::CgoExterns; use super::types::*; -// Generic executor; uses the current (v3) engine types +// Generic executor; uses the current engine types pub trait CgoExecutor: Send { fn execute_message( &mut self, @@ -26,6 +26,8 @@ pub trait CgoExecutor: Send { ) -> anyhow::Result; fn flush(&mut self) -> anyhow::Result; + + fn dump_cache(&mut self, bs: CgoBlockstore) -> anyhow::Result<()>; } pub struct Config { @@ -148,6 +150,11 @@ mod v4 { fn flush(&mut self) -> anyhow::Result { fvm4::executor::Executor::flush(self) } + + fn dump_cache(&mut self, bs: CgoBlockstore) -> anyhow::Result<()> { + log::info!("CgoExecutor4::dump_cache"); + fvm4::executor::Executor::dump_cache(self, bs) + } } impl AbstractMultiEngine for MultiEngine4 { @@ -420,6 +427,11 @@ mod v3 { fn flush(&mut self) -> anyhow::Result { fvm3::executor::Executor::flush(self) } + + // dump_cache is only implemented in v4 + fn dump_cache(&mut self, _: CgoBlockstore) -> anyhow::Result<()> { + Err(anyhow::anyhow!("dump_cache not implemented for fvm v3")) + } } impl AbstractMultiEngine for MultiEngine3 { @@ -681,6 +693,11 @@ mod v2 { fn flush(&mut self) -> anyhow::Result { fvm2::executor::Executor::flush(self) } + + // dump_cache is only implemented in v4 + fn dump_cache(&mut self, _: CgoBlockstore) -> anyhow::Result<()> { + Err(anyhow::anyhow!("dump_cache not implemented for fvm v2")) + } } impl AbstractMultiEngine for MultiEngine2 { diff --git a/rust/src/fvm/machine.rs b/rust/src/fvm/machine.rs index c12ae8ec..fefe9559 100644 --- a/rust/src/fvm/machine.rs +++ b/rust/src/fvm/machine.rs @@ -398,6 +398,23 @@ fn fvm_machine_execute_message( }) } +#[ffi_export] +fn fvm_machine_dump_cache( + executor: &'_ InnerFvmMachine, + blockstore_id: u64, +) -> repr_c::Box> { + catch_panic_response("fvm_machine_dump_cache", || { + let blockstore = CgoBlockstore::new(blockstore_id); + let mut executor = executor + .machine + .as_ref() + .context("missing executor")? + .lock() + .map_err(|e| anyhow!("executor lock poisoned: {e}"))?; + executor.dump_cache(blockstore) + }) +} + #[ffi_export] fn fvm_machine_flush(executor: &'_ InnerFvmMachine) -> repr_c::Box>> { catch_panic_response("fvm_machine_flush", || { @@ -422,6 +439,7 @@ destructor!( ); destructor!(destroy_fvm_machine_flush_response, Result>); +destructor!(destroy_fvm_machine_dump_cache_response, Result<()>); #[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] struct LotusGasCharge { @@ -439,6 +457,7 @@ struct Trace { pub msg_invoked: Option, pub gas_charges: Vec, pub subcalls: Vec, + pub logs: Vec, } #[derive(Serialize_tuple, Deserialize_tuple, Debug, PartialEq, Eq, Clone)] @@ -499,6 +518,7 @@ fn build_lotus_trace( }, gas_charges: vec![], subcalls: vec![], + logs: vec![], }; while let Some(trace) = trace_iter.next() { @@ -568,6 +588,9 @@ fn build_lotus_trace( .unwrap_or(u64::MAX), }); } + ExecutionEvent::Log(s) => { + new_trace.logs.push(s); + } _ => (), // ignore unknown events. }; } @@ -605,10 +628,12 @@ mod test { ExecutionEvent::GasCharge(initial_gas_charge.clone()), call_event.clone(), return_result.clone(), + ExecutionEvent::Log("something happened".to_string()), call_event.clone(), call_event, return_result.clone(), return_result.clone(), + ExecutionEvent::Log("something else happened".to_string()), return_result, ]; @@ -648,5 +673,8 @@ mod test { assert_eq!(lotus_trace.subcalls[0].subcalls.len(), 0); assert_eq!(lotus_trace.subcalls[1].subcalls.len(), 1); assert_eq!(lotus_trace.subcalls[1].subcalls[0].subcalls.len(), 0); + assert_eq!(lotus_trace.logs.len(), 2); + assert_eq!(lotus_trace.logs[0], "something happened"); + assert_eq!(lotus_trace.logs[1], "something else happened"); } }