-
Notifications
You must be signed in to change notification settings - Fork 140
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
add support for actor events. #1049
Changes from 10 commits
277aa3c
912e65b
8a5d538
2c2d958
b498f03
19f88c3
56b2679
7d72eee
cc60eea
d0648f2
f18b9d9
ab4880b
da15510
40bf949
9c29e6e
bd35a04
041ea1a
1949ba1
4b93932
3d2b971
f1ac14b
44cfc40
10e4f51
7d1a62f
f380575
547d8c2
3a8d2e5
a19b83c
240b7e5
30c9d71
02e7944
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ use fvm_shared::address::Address; | |
use fvm_shared::address::Payload; | ||
use fvm_shared::econ::TokenAmount; | ||
use fvm_shared::error::{ErrorNumber, ExitCode}; | ||
use fvm_shared::event::StampedEvent; | ||
use fvm_shared::message::Message; | ||
use fvm_shared::receipt::Receipt; | ||
use fvm_shared::ActorID; | ||
|
@@ -19,13 +20,14 @@ use crate::call_manager::{backtrace, CallManager, InvocationResult}; | |
use crate::gas::{Gas, GasCharge, GasOutputs}; | ||
use crate::kernel::{Block, ClassifyResult, Context as _, ExecutionError, Kernel}; | ||
use crate::machine::{Machine, BURNT_FUNDS_ACTOR_ADDR, REWARD_ACTOR_ADDR}; | ||
use crate::trace::ExecutionTrace; | ||
|
||
/// The default [`Executor`]. | ||
/// | ||
/// # Warning | ||
/// | ||
/// Message execution might run out of stack and crash (the entire process) if it doesn't have at | ||
/// least 64MiB of stacks space. If you can't guarantee 64MiB of stack space, wrap this executor in | ||
/// least 64MiB of stack space. If you can't guarantee 64MiB of stack space, wrap this executor in | ||
/// a [`ThreadedExecutor`][super::ThreadedExecutor]. | ||
// If the inner value is `None` it means the machine got poisoned and is unusable. | ||
#[repr(transparent)] | ||
|
@@ -66,46 +68,62 @@ where | |
}; | ||
|
||
// Apply the message. | ||
let (res, gas_used, mut backtrace, exec_trace) = self.map_machine(|machine| { | ||
// We're processing a chain message, so the sender is the origin of the call stack. | ||
let mut cm = K::CallManager::new( | ||
machine, | ||
msg.gas_limit, | ||
sender_id, | ||
msg.sequence, | ||
msg.gas_premium.clone(), | ||
); | ||
// This error is fatal because it should have already been accounted for inside | ||
// preflight_message. | ||
if let Err(e) = cm.charge_gas(inclusion_cost) { | ||
return (Err(e), cm.finish().1); | ||
} | ||
|
||
let params = if msg.params.is_empty() { | ||
None | ||
} else { | ||
Some(Block::new(DAG_CBOR, msg.params.bytes())) | ||
}; | ||
let (res, gas_used, mut backtrace, exec_trace, events_root, events) = | ||
self.map_machine(|machine| { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we've reached the point where we may want to just define a struct (we can define one inside this function). Even if we destructure immediately, it'll get us nice named fields and prevent potential mistakes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I can do that in this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// We're processing a chain message, so the sender is the origin of the call stack. | ||
let mut cm = K::CallManager::new( | ||
machine, | ||
msg.gas_limit, | ||
sender_id, | ||
msg.sequence, | ||
msg.gas_premium.clone(), | ||
); | ||
// This error is fatal because it should have already been accounted for inside | ||
// preflight_message. | ||
if let Err(e) = cm.charge_gas(inclusion_cost) { | ||
return (Err(e), cm.finish().1); | ||
} | ||
|
||
let result = cm.with_transaction(|cm| { | ||
// Invoke the message. | ||
let ret = cm.send::<K>(sender_id, msg.to, msg.method_num, params, &msg.value)?; | ||
let params = if msg.params.is_empty() { | ||
None | ||
} else { | ||
Some(Block::new(DAG_CBOR, msg.params.bytes())) | ||
}; | ||
|
||
// Charge for including the result (before we end the transaction). | ||
if let InvocationResult::Return(value) = &ret { | ||
cm.charge_gas(cm.context().price_list.on_chain_return_value( | ||
value.as_ref().map(|v| v.size() as usize).unwrap_or(0), | ||
))?; | ||
} | ||
let result = cm.with_transaction(|cm| { | ||
// Invoke the message. | ||
let ret = | ||
cm.send::<K>(sender_id, msg.to, msg.method_num, params, &msg.value)?; | ||
|
||
// Charge for including the result (before we end the transaction). | ||
if let InvocationResult::Return(value) = &ret { | ||
cm.charge_gas(cm.context().price_list.on_chain_return_value( | ||
value.as_ref().map(|v| v.size() as usize).unwrap_or(0), | ||
))?; | ||
} | ||
|
||
Ok(ret) | ||
}); | ||
let (res, machine) = cm.finish(); | ||
|
||
// Flush all events to the store. | ||
let events_root = match machine.commit_events(res.events.as_slice()) { | ||
raulk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok(cid) => cid, | ||
Err(e) => return (Err(e), machine), | ||
}; | ||
|
||
Ok(ret) | ||
}); | ||
let (res, machine) = cm.finish(); | ||
( | ||
Ok((result, res.gas_used, res.backtrace, res.exec_trace)), | ||
machine, | ||
) | ||
})?; | ||
( | ||
Ok(( | ||
result, | ||
res.gas_used, | ||
res.backtrace, | ||
res.exec_trace, | ||
events_root, | ||
res.events, | ||
)), | ||
machine, | ||
) | ||
})?; | ||
|
||
// Extract the exit code and build the result of the message application. | ||
let receipt = match res { | ||
|
@@ -121,6 +139,7 @@ where | |
exit_code: ExitCode::OK, | ||
return_data, | ||
gas_used, | ||
events_root, | ||
} | ||
} | ||
Ok(InvocationResult::Failure(exit_code)) => { | ||
|
@@ -131,12 +150,14 @@ where | |
exit_code, | ||
return_data: Default::default(), | ||
gas_used, | ||
events_root, | ||
} | ||
} | ||
Err(ExecutionError::OutOfGas) => Receipt { | ||
exit_code: ExitCode::SYS_OUT_OF_GAS, | ||
return_data: Default::default(), | ||
gas_used, | ||
events_root, | ||
}, | ||
Err(ExecutionError::Syscall(err)) => { | ||
// Errors indicate the message couldn't be dispatched at all | ||
|
@@ -153,6 +174,7 @@ where | |
exit_code, | ||
return_data: Default::default(), | ||
gas_used, | ||
events_root, | ||
} | ||
} | ||
Err(ExecutionError::Fatal(err)) => { | ||
|
@@ -177,6 +199,7 @@ where | |
exit_code: ExitCode::SYS_ASSERTION_FAILED, | ||
return_data: Default::default(), | ||
gas_used: msg.gas_limit, | ||
events_root, | ||
} | ||
} | ||
}; | ||
|
@@ -188,12 +211,9 @@ where | |
}; | ||
|
||
match apply_kind { | ||
ApplyKind::Explicit => self | ||
.finish_message(msg, receipt, failure_info, gas_cost) | ||
.map(|mut apply_ret| { | ||
apply_ret.exec_trace = exec_trace; | ||
apply_ret | ||
}), | ||
ApplyKind::Explicit => { | ||
self.finish_message(msg, receipt, failure_info, gas_cost, exec_trace, events) | ||
} | ||
ApplyKind::Implicit => Ok(ApplyRet { | ||
msg_receipt: receipt, | ||
penalty: TokenAmount::zero(), | ||
|
@@ -205,6 +225,7 @@ where | |
gas_burned: 0, | ||
failure_info, | ||
exec_trace, | ||
events, | ||
}), | ||
} | ||
} | ||
|
@@ -367,6 +388,8 @@ where | |
receipt: Receipt, | ||
failure_info: Option<ApplyFailure>, | ||
gas_cost: TokenAmount, | ||
exec_trace: ExecutionTrace, | ||
events: Vec<StampedEvent>, | ||
) -> anyhow::Result<ApplyRet> { | ||
// NOTE: we don't support old network versions in the FVM, so we always burn. | ||
let GasOutputs { | ||
|
@@ -425,7 +448,8 @@ where | |
gas_refund, | ||
gas_burned, | ||
failure_info, | ||
exec_trace: vec![], | ||
exec_trace, | ||
events, | ||
}) | ||
} | ||
|
||
|
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.
We need to handle reverts/aborts somehow.
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.
Yeah, this is unfortunately a bit of a can of worms.
I can think of two ways to handle this:
I think (2) gives us the most flexibility.
Ultimately I propose we solve this in Sapphire (and I can own it myself). We can ship Topaz with this caveat.
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.
No, Filecoin totally reverts everything on non-zero exit codes.
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.
^What Steb said -- we do so in both the legacy VM and the FVM.
I think that makes most (all?) of the problems you're describing go away.
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.
I was in doubt and it sounded strange that it didn't, but I wasn't able to find where it was happening. It happens through the layering in the state tree:
ref-fvm/fvm/src/call_manager/default.rs
Line 160 in 0801fd8