Skip to content
Open
48 changes: 46 additions & 2 deletions fvm/src/call_manager/default.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use std::rc::Rc;
use std::sync::{Arc, Mutex};

use anyhow::{Context, anyhow};
use cid::Cid;
Expand All @@ -22,6 +23,7 @@ use crate::call_manager::FinishRet;
use crate::call_manager::backtrace::Frame;
use crate::eam_actor::EAM_ACTOR_ID;
use crate::engine::Engine;
use crate::executor::ReservationSession;
use crate::gas::{Gas, GasTracker};
use crate::kernel::{
Block, BlockRegistry, ClassifyResult, ExecutionError, Kernel, Result, SyscallError,
Expand Down Expand Up @@ -77,6 +79,8 @@ pub struct InnerDefaultCallManager<M: Machine> {
events: EventsAccumulator,
/// The actor call stack (ActorID and entrypoint name tuple).
actor_call_stack: Vec<(ActorID, &'static str)>,
/// Shared reservation session ledger for the current tipset, if any.
reservation_session: Arc<Mutex<ReservationSession>>,
}

#[doc(hidden)]
Expand Down Expand Up @@ -111,6 +115,7 @@ where
receiver_address: Address,
nonce: u64,
gas_premium: TokenAmount,
reservation_session: Arc<Mutex<ReservationSession>>,
) -> Self {
let limits = machine.new_limiter();
let gas_tracker =
Expand Down Expand Up @@ -162,6 +167,7 @@ where
events: Default::default(),
state_access_tracker,
actor_call_stack: vec![],
reservation_session,
})))
}

Expand Down Expand Up @@ -489,8 +495,46 @@ where
.get_actor(from)?
.ok_or_else(||syscall_error!(InsufficientFunds; "insufficient funds to transfer {value}FIL from {from} to {to})"))?;

if &from_actor.balance < value {
return Err(syscall_error!(InsufficientFunds; "sender does not have funds to transfer (balance {}, transfer {})", &from_actor.balance, value).into());
// In reservation mode, ensure the sender cannot spend funds that are reserved for gas.
// Free balance is defined as balance - reserved_remaining. To avoid negative intermediates,
// we enforce the equivalent inequality: value + reserved_remaining <= balance.
let (reservation_open, reserved_remaining) = {
let session = self
.reservation_session
.lock()
.expect("reservation session mutex poisoned");
if session.open {
let reserved = session
.reservations
.get(&from)
.cloned()
.unwrap_or_else(TokenAmount::zero);
(true, reserved)
} else {
(false, TokenAmount::zero())
}
};

if reservation_open {
let required = &reserved_remaining + value;
if from_actor.balance < required {
return Err(syscall_error!(
InsufficientFunds;
"sender does not have free funds to transfer (balance {}, transfer {}, reserved {})",
&from_actor.balance,
value,
&reserved_remaining
)
.into());
}
} else if &from_actor.balance < value {
return Err(syscall_error!(
InsufficientFunds;
"sender does not have funds to transfer (balance {}, transfer {})",
&from_actor.balance,
value
)
.into());
}

if from == to {
Expand Down
4 changes: 4 additions & 0 deletions fvm/src/call_manager/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use std::sync::{Arc, Mutex};

use cid::Cid;
use fvm_ipld_encoding::{CBOR, to_vec};
use fvm_shared::address::Address;
Expand All @@ -10,6 +12,7 @@ use fvm_shared::{ActorID, METHOD_CONSTRUCTOR, MethodNum};

use crate::Kernel;
use crate::engine::Engine;
use crate::executor::ReservationSession;
use crate::gas::{Gas, GasCharge, GasTimer, GasTracker, PriceList};
use crate::kernel::{self, BlockRegistry, ClassifyResult, Context, Result};
use crate::machine::{Machine, MachineContext};
Expand Down Expand Up @@ -60,6 +63,7 @@ pub trait CallManager: 'static {
receiver_address: Address,
nonce: u64,
gas_premium: TokenAmount,
reservation_session: Arc<Mutex<ReservationSession>>,
) -> Self;

/// Calls an actor at the given address and entrypoint. The type parameter `K` specifies the the _kernel_ on top of which the target
Expand Down
Loading
Loading