Customizing a subnet
Tutorial to add a new syscall to Filecoin Virtual Machine and create a new built-in actor in IPC to activate the syscall
IPC is uniquely hyper customizable as a scalability framework. Subnets are highly customizable and can be temporal, allowing subnet operators to spin up customized subnets for various needs, including modular consensus, gas option, configurable chain primitives, customized features vis pluggable syscalls and build-in actor.
In this tutorial, we will focus on how to extend features to your IPC subnet by customizing syscalls that are 'pluggable' as needed.
IPC uses the Filecoin Virtual Machine (FVM) as its execution layer, which is a WASM-based polyglot VM. FVM exposes all system features and information through syscalls, as part of its SDK. The FVM SDK is designed to be pluggable to enable user-defined custom features with syscalls, while implementing default features from FVM kernel. Use cases includes:
Extending chain-specific syscalls once IPC supports more root chains. Because other chains may have their own special syscalls different as Filecoin (proof validation, etc.).
Extending features to support better development tools. E.g. adding special debugging syscalls, adding randomness syscalls, and supporting more ECC curve, etc.
Pre-requisite knowledge for tutorial
IPC/fendermint implementation
Rust
Instructions
These instructions describe the steps required to create a new kernel which implements a new syscall along with an example built-in actor that shows how you would call that syscall. Full example here.
1. Define the custom syscall
In this example, we will be creating a simple syscall which accesses the filesystem. Inside syscalls, you can run external processes, link to rust libraries, access network, call other syscalls, etc.
We’ll call this new syscall
my_custom_syscalland its defined as follows:fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L23
Define a struct
CustomKernelImplwhich extendsDefaultKernel. We use theambassadorcrate to automatically delegate calls which reduces the boilerplate code we need to write. Here we simply delegate all calls to existing syscall to theDefaultKernel.fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L27
2. Implementing all necessary functions for the syscall
Implement
my_custom_syscallHere is where we implement our custom syscall:
fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L42
Next we need to implement the
Kerneltrait for the newCustomKernelImpl. You can treat this as boilerplate code and you can just copy it as is:
fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L61
3. Link syscalls to the kernel
Next we need to implement the
SyscallHandlertrait for theCustomKernelImpland link all the syscalls to that kernel. We need to explicitly list each of the syscall traits (ActorOps, SendOps, etc) manually here in addition to theCustomKerneltrait. Then inside thelink_syscallsmethod we plug in the actor invocation to the kernel function that should process that syscall. We can link all the existing syscalls using thelink_syscallson theDefaultKerneland then link our custom syscall.
fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L112
4. Expose the customized syscall
Once this function is linked to a syscall and exposed publicly, we can use this syscall by calling
my_custom_kernel.my_custom_syscall
fendermint/vm/interpreter/src/fvm/examples/mycustomkernel.rs#L136
5. Replace existing IPC kernel with new custom kernel
Since the customized syscall is implemented in a
CustomKernelImplwhich extends and implements all the behaviors forDefaultKernel, we can plug it into IPC instead ofDefaultKernel\To use this kernel in fendermint code, replace
DefaultKernelwithCustomKernelImplfor theexecutordeclaration infendermint/vm/interpreter/src/fvm/state/exec.rs
fendermint/vm/interpreter/src/fvm/state/exec.rs#L86
6. Use syscall in your IPC subnet
Now, we are all set to use the custom syscall in the IPC subnet. The custom syscall can be called in IPC actors to utilize the extended feature. For this tutorial, we can create a simple actor to demonstrate how to import and call the custom syscall and then confirm that its working correctly.
Let’s create a
customsyscallfolder inipc/fendermint/actors/and then create a file called actor.rs in that new folder. Here we want to create a very simple actor, which when invoked (received a message on its Invoke method) will call the new syscall and return its value:
fendermint/actors/customsyscall/src/actor.rs#L14
Even though this is Rust code, IPC will compile it as a Wasm target and then run the compiled Wasm code inside FVM as an actor. However, we want to share some of the code between Wasm and IPC, such as the actor name
CUSTOMSYSCALL_ACTOR_NAMEand theInvokemethod enum. We will define these in a separate file calledshared.rsas follows:
fendermint/actors/customsyscall/src/shared.rs
We next need to write a
lib.rsfile which exports the shared code and only compilesactor.rsif we are building the Wasm actor.
fendermint/actors/customsyscall/src/lib.rs
7. Load and deploy actor at genesis
We have so far created a new kernel and syscall, switched IPC to use that kernel and created an actor which calls the new syscall. However, in order to call this actor in IPC, we must load it from the custom_actors_bundle.
To do this open
fendermint/vm/interpreter/src/fvm/genesis.rsfile and in theinitfunction add our customsyscall actor right after creating thechainmetadataactor:
fendermint/vm/interpreter/src/fvm/genesis.rs#L251
Your actor has now been deployed and we should be able to send it messages!
8. Invoke the actor
In the last step in this tutorial we will send our customsyscall actor messages which will cause it to run its Invoke method and execute the custom syscall. Here, we will simply call it for every new block height. Go to fendermint/vm/interpreter/src/fvm/exec.rs and inside the begin function add the following code:
fendermint/vm/interpreter/src/fvm/exec.rs#L115
This code sends a message to the customsyscall actor and parses it output after it has been executed. We print out the return value from the actor, which will be the return value of our custom syscall.
9. Test your actor
In order to see this working end to end in IPC, you can run one of our integration tests. These tests run IPC in docker containers so make sure to have docker installed on your machine if you are following along.
We must first need to build a new docker container for the fendermint image which will contain all the code you have added so for. To do this run:
After the fendermint docker image has been built, you can run one of the integration tests
View fendermint logs and see the output generated by calling the customsyscall actor in each epoch:
View the docker logs:
You can now run cargo make teardown to stop the containers.
Last updated