-
Notifications
You must be signed in to change notification settings - Fork 136
Multistage execution: Add reservation Begin/End FFI bindings #554
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds FFI bindings for FVM reservation session management, enabling Lotus to orchestrate gas reservations at tipset boundaries. The implementation spans Rust FFI exports, CGO wrappers, and Go public APIs.
Key Changes:
- Adds
FVM_BeginReservationsandFVM_EndReservationsFFI exports with status codes and error messages - Introduces reservation error types and status-to-error mapping in both Rust and Go
- Implements error message allocation/deallocation across the FFI boundary
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/src/fvm/machine.rs | Implements FFI exports for Begin/End reservation sessions, error message handling, and tracks current machine in global state |
| rust/src/fvm/engine.rs | Defines ReservationError enum and default trait methods for reservation operations |
| cgo/fvm.go | Provides CGO wrappers that invoke C ABI and manage error message memory |
| fvm.go | Exposes typed reservation errors and high-level BeginReservations/EndReservations methods on FVM instances |
| cgo/fvm_reservations_test.go | Tests error message handling for invalid reservation calls |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Removes the CURRENT_MACHINE global in Rust and passes the executor pointer explicitly through CGO to Rust. This prevents safety issues with concurrent FVM instances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const LOTUS_FVM_CONCURRENCY_ENV_NAME: &str = "LOTUS_FVM_CONCURRENCY"; | ||
| const VALID_CONCURRENCY_RANGE: RangeInclusive<u32> = 1..=256; | ||
|
|
||
| #[derive_ReprC] |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The derive_ReprC attribute appears to be used here, but it's not a standard Rust derive macro. This seems to be from the safer_ffi crate or similar. Please verify that this crate is properly included in dependencies and that this is the intended FFI representation mechanism.
| // Enforce a maximum encoded plan size (256 KiB). | ||
| // This is based on a maximum of 4096 entries at 64 bytes per entry. | ||
| const MAX_PLAN_BYTES: usize = 256 * 1024; |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MAX_PLAN_BYTES constant of 256 KiB (based on 4096 entries × 64 bytes) should be validated against the actual CBOR encoding size of a Vec<(Address, TokenAmount)>. The comment suggests 64 bytes per entry, but a CBOR-encoded Address (41+ bytes) plus TokenAmount (variable) plus CBOR overhead may exceed this estimate, especially for addresses with longer protocols or large token amounts. Consider adding a test that verifies a plan with 4096 entries actually fits within this limit.
| let plan: Vec<(Address, TokenAmount)> = match fvm_ipld_encoding::from_slice(bytes) { | ||
| Ok(p) => p, | ||
| Err(_) => { | ||
| set_reservation_error_message_out( | ||
| error_msg_ptr_out, | ||
| error_msg_len_out, | ||
| "reservation invariant violated: invalid plan CBOR encoding", | ||
| ); | ||
| return FvmReservationStatus::ErrReservationInvariant; | ||
| } |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CBOR decoding error at line 643 is classified as ErrReservationInvariant with message "invalid plan CBOR encoding". However, this is not an invariant violation—it's invalid input from the caller. Consider either: (1) Adding a new error variant like InvalidPlanEncoding to distinguish client errors from internal invariant violations, or (2) Accepting any decoding error as OK with an empty-plan semantics if the intent is to be lenient. The current classification may make debugging harder by conflating different error categories.
| fn reservation_error_mapping_sets_message_for_insufficient_funds() { | ||
| let mut msg_ptr: *const u8 = std::ptr::null(); | ||
| let mut msg_len: usize = 0; | ||
|
|
||
| let status = map_reservation_error_to_status( | ||
| ReservationError::InsufficientFundsAtBegin { sender: 42 }, | ||
| &mut msg_ptr, | ||
| &mut msg_len, | ||
| ); | ||
|
|
||
| assert_eq!(status, FvmReservationStatus::ErrInsufficientFundsAtBegin); | ||
| assert!(!msg_ptr.is_null()); | ||
| assert!(msg_len > 0); | ||
|
|
||
| let msg = unsafe { | ||
| let slice = std::slice::from_raw_parts(msg_ptr, msg_len); | ||
| std::str::from_utf8(slice).expect("reservation error message is valid UTF-8") | ||
| }; | ||
| assert!( | ||
| msg.contains("sender 42"), | ||
| "expected message to contain sender id, got {msg}" | ||
| ); | ||
|
|
||
| FVM_DestroyReservationErrorMessage(msg_ptr as *mut u8, msg_len); | ||
| } |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The map_reservation_error_to_status function at line 972-976 tests InsufficientFundsAtBegin with a hardcoded sender ID of 42, but the test doesn't verify that this sender ID actually appears in the returned error message. The assertion at line 987 uses contains("sender 42") which could pass even if the formatting is incorrect. Consider adding an assertion that checks for the exact expected format or at minimum verifies the sender ID is present as a number.
Summary
This PR adds FFI bindings for the new tipset reservation session API required by the Multi‑Stage Tipset Gas Reservations work.
Specifically, it:
cgo/fvm.goto call the reservation API and surface structured errors to Lotus.reservationsmodule inrust/src/fvmand hooks it intomod.rs.These bindings are used by Lotus to start and end a reservation session around tipset execution while keeping the reservation implementation inside ref‑fvm.
Motivation
Lotus and ref‑fvm are adding tipset‑scope gas reservations to eliminate miner‑penalty exposure from intra‑tipset self‑drain scenarios, as described in the draft FIP
“Multi‑Stage Tipset Gas Reservations”.
filecoin‑ffi needs to expose the engine’s reservation session API (Begin/End and error reporting) to Go so that Lotus can orchestrate reservations at the tipset boundary
without introducing new on‑chain actors or migrations.
Changes
cgo/fvm.go:cgo/fvm_reservations.go:rust/src/fvm/mod.rs:reservationssubmodule.rust/src/fvm/reservations.rs:Testing
go test ./...in this repo.extern/filecoin-ffisubmodule and ran the existing FVM/consensus test suites that exercise FFI calls.