ref-fvm
ref-fvm copied to clipboard
Rethink `invoke` signature
Currently, invoke's signature is:
fn invoke(params_id: i32) -> i32 /* ret_id */;
However:
- It would be nice to take the method number in the parameters.
- It would be nice to be able to return an error instead of only being able to long-jump
exit:
fn invoke(method_num: i32, params_id: i32) -> (i32 /* exit code */, i32 /* ret_id */);
But to do this, we'd need to investigate wasm support for multiple return values. Last time we checked, it wasn't great. But then again, we were trying to use multiple return values in syscalls. Using multiple return values to return to the host is likely significantly better (there are existing wrapper shims for rust that will just "make this work").
We can also take it even further:
fn invoke(
method_num: i32,
params_id: i32,
params_len: i32,
params_codec: u64,
) -> (i32 /* exit code */, i32 /* ret_id */);
or even
fn invoke(
from: u64,
to: u64,
method_num: i32,
params_id: i32,
params_len: i32,
params_codec: u64,
) -> (i32 /* exit code */, i32 /* ret_id */);
So, I've been thinking about this for a bit:
- We're introducing a new entrypoint for constructors: https://github.com/filecoin-project/ref-fvm/issues/746.
- We're going to be doing the same thing for upgrades.
And in both cases, we're trying to keep the API the same fn(param_block: i32) -> i32 /* ret block */.
It would be nice to keep the general signature of "IPLD-block in, IPLD-block out" the same in all cases.
NOTE: I could see a benefit of adding an error number to the return value... or we could, and probably should, treat negative return values as error codes.
An alternative to the whole "multiple entrypoint" saga ("invoke", "constructor", "upgrade", and eventually "validate") is to reserve negative method numbers for "system" entrypoints. I.e.:
- Users can invoke actors with methods 0+
- The system can invoke actors with methods <0:
invoke(-1, params)-> constructor.invoke(-2, params)-> upgrade.invoke(-3, params)-> validate.
But... I hate it.
We could also change the signature to:
fn some_entrypoint(
params_id: i32,
params_len: i32,
params_codec: u64,
) -> i32 /* ret id or error */
That means we don't have to "stat" the block to get the length/codec and lets us keep the same parameters across all entrypoints (saving a syscall).
It doesn't solve the issue of not passing the method number.
We (@arajasek and @fridrik01 and I) talked about this and agreed on... negative method numbers. Because it:
- Reduces complexity (one entry point).
- Clearly calls out which methods are special "system" methods (we forbid users from calling these method numbers).
The full signature I plan to propose is:
fn invoke(method_num: i64, param_id: u32, param_codec: u64, param_len: u32) -> u32 /* ret block */;
However, still TBD, is how to handle errors. Unfortunately, non-zero exit codes can coexist with error return values (a required feature for EVM support). So... yeah...
I'd like to propose:
fn invoke(method_num: i64, param_id: u32, param_codec: u64, param_len: u32) -> (u32 /* return value id */, u32 /* exit code */);
But implementing this method can be tricky in most languages as it requires multiple return-value support in the language's wasm toolchain.