-
Notifications
You must be signed in to change notification settings - Fork 141
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
Pluggable System Calls & Kernel #1787
Comments
@Stebalien Would like to discuss the approach and impl. |
impl Kernel for ... where Kernel
However, this has a few problems:
We'll probably need to:
pub trait IpldBlockOps {
fn block_open(&mut self, cid: &Cid) -> Result<(BlockId, BlockStat)>;
// ...
}
pub trait BaseKernel {
type IpldBlockOps: IpldBlockOps;
type CryptoOps: CryptoOps;
pub fn ipld(&mut self) -> &mut Self::IpldBlockOps;
pub fn crypto(&mut self) -> &mut Self::CryptoOps;
// ...
}
pub trait MyKernelExtension: BaseKernel {
type CustomOps: CustomOps;
pub fn custom(&mut self) -> &mut Self::CustomOps;
} This way, the user can override specific features/modules without having to wrap the entire kernel trait. |
So, first some rust context and a discussion of "patterns".
In rust, you'd usually do something like: trait MyTrait {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
fn e(&self);
fn f(&self);
}
struct Base;
impl MyTrait for Base {
fn a(&self) { /* do something */ }
fn b(&self) { /* do something */ }
fn c(&self) { /* do something */ }
fn d(&self) { /* do something */ }
fn e(&self) { /* do something */ }
fn f(&self) { /* do something */ }
}
struct SubClass(Base);
impl MyTrait for Base {
fn a(&self) { self.0.a() }
fn b(&self) { self.0.b() }
fn c(&self) { self.0.c() }
fn d(&self) { self.0.d() }
fn e(&self) { self.0.e() }
fn f(&self) { /* override */ }
} That is, manually delegate every method you're not overriding. Or, you'd use something like ambassador to do this automatically (kind of). But... there's another pattern we can explore. We can break the trait up into multiple traits (as we do in the Kernel today), but then, instead of defining the trait GroupOne {
fn a(&self);
fn b(&self);
fn c(&self);
}
trait GroupTwo {
fn d(&self);
fn e(&self);
fn f(&self);
}
trait MyTrait {
type GroupOne: GroupOne;
type GroupTwo: GroupTwo;
fn group_one(&self) -> &Self::GroupOne;
fn group_two(&self) -> &Self::GroupTwo;
}
struct Base;
impl MyTrait for Base {
type GroupOne: GroupOne = Self;
type GroupTwo: GroupTwo = Self;
fn group_one(&self) -> &Self::GroupOne { self }
fn group_two(&self) -> &Self::GroupTwo { self }
}
impl GroupOne for Base {
fn a(&self) { /* do something */ }
fn b(&self) { /* do something */ }
fn c(&self) { /* do something */ }
}
impl GroupTwo for Base {
fn d(&self) { /* do something */ }
fn e(&self) { /* do something */ }
fn f(&self) { /* do something */ }
}
struct SubClass(Base);
impl MyTrait for Base {
type GroupOne: GroupOne = Self;
type GroupTwo: GroupTwo = Self;
fn group_one(&self) -> &Self::GroupOne { self }
fn group_two(&self) -> &Self::GroupTwo { self }
}
impl GroupTwo for SubClass {
fn d(&self) { self.0.d() }
fn e(&self) { self.0.e() }
fn f(&self) { /* override */ }
} This is definitely more complex but, it makes it much easier to override/extend specific groups. With today's 12 traits, we can forward ~12 very simple methods instead of ~40. Even better, if we re-organize according to filecoin-project/FIPs#845 (approximately), we can forward 5/6 methods. |
In terms of sequencing, I'd actually start with the price-list.
|
You know what? No, I change my mind. Let's not start with the price-list. I'd like to do that, but I'm not entirely sure how much of the price-list the user should actually care about and/or specify. Let's start small and ignore that entire pattern I described above. That pattern is important for overriding functionality, but we're just extending functionality here:
Then, try extracting the "filcrypto" specific syscalls ( #[delegatable_trait]
pub trait Kernel {
// ...
}
#[derive(Delegate)]
#[delegate(Kernel)]
pub struct FilecoinKernel<K=DefaultKernel>(K) where K: Kernel;
impl<K> SyscallHandler for FilecoinKernel<K> where K: Kernel {
fn bind_syscalls(&self, linker: &mut Linker<InvocationData<impl Kernel + 'static>>) -> Result<()> {
// Bind the default syscalls.
self.0.bind_syscalls(linker)?;
// Now bind the crypto syscalls.
}
}
// We _might_ want to make a `FilecoinKernel` trait and a separate implementation to make it easier to test... but let's start with a direct implementation if it's easier.
impl<K> FilecoinKernel<K> where K: Kernel {
fn compute_unsealed_sector_cid(
&self,
proof_type: RegisteredSealProof,
pieces: &[PieceInfo],
) -> Result<Cid> { ... }
fn verify_post(&self, verify_info: &WindowPoStVerifyInfo) -> Result<bool> { ... }
fn verify_consensus_fault(
&self,
h1: &[u8],
h2: &[u8],
extra: &[u8],
) -> Result<Option<ConsensusFault>> { ... }
fn batch_verify_seals(&self, vis: &[SealVerifyInfo]) -> Result<Vec<bool>> { ... }
fn verify_aggregate_seals(&self, aggregate: &AggregateSealVerifyProofAndInfos) -> Result<bool> { ... }
fn verify_replica_update(&self, replica: &ReplicaUpdateInfo) -> Result<bool> { ... }
} |
See: #1787 This PR adds initial support for pluggable syscall by allowing us to extend the default kernel and add new custom syscalls through a new kernel. The implementation does this by moving the global bind_syscall function into kernel via a SyscallHandler trait where each kernel needs to add their new syscalls. The implementation uses Ambassador crate which automatically create stubs for delegating kernel implementations we are not overriding in new kernels, minimizing the code we need to write.
We now have the basic implementation. However:
|
For context, we merged #1922 because it (a) works, (b) is progress, and (c) was getting large. Not because the feature is "complete". The concrete next step is to test this change out with IPC to see if it does what we need it to do. |
As this is being integrated to IPC I'd like to explore the discussion of adding this to stable FVM version. At the moment IPC is pointing to a branch in
@Stebalien @fridrik01 Are there any other steps related to stability, API, testing, adding missing features that you have in mind before we mark it as stable? |
Everything should be in master and I'm currently working on a release (trying to get some final changes into the AMT first, but I guess I should just release FVM itself). |
@Stebalien thanks! Please ping @fridrik01 after you release it so he can use it in IPC instead of pointing to the branch. |
Fixed & released. |
Right now, the FVM provides a single system call API that's difficult to extend. Use-cases include:
Proposal:
syscalls::bind_syscalls
function pluggable. E.g., consider making it a possibly static method on the kernel?Kernel
extensible. I.e., define a base kernel that must be implemented (along with a basicbind_syscalls
function) and let the user define custom "mixins".The text was updated successfully, but these errors were encountered: