FVM Actors and Getters (view functions) #383
Replies: 8 comments 10 replies
-
cc @ZenGround0 @arajasek for 👀 this convo. |
Beta Was this translation helpful? Give feedback.
-
Client applications don't need to pay gas to execute methods on actors locally. You only need to pay when you want a message included and executed by everyone, but nothing is preventing you from executing messages on your own node/hosted node. |
Beta Was this translation helpful? Give feedback.
-
I understand the motivation for accessing state off-chain. I do not understand the following:
Could you explain this more for me? As far as I understand, in all ecosystems, if a contract wants to get data from another contract's state it is going to need to pay gas, either for a method invocation or perhaps for some method of direct access. But loading the state is computation that must be paid for. I think there might be some confusion over the "view functions are free" meme – they're not free when called by other contracts as part of a transaction. |
Beta Was this translation helpful? Give feedback.
-
Thanks @Schwartz10 this is a great question, which pre-dates the FVM too. In my opinion, actors should implement methods to expose their state to other contracts and local callers. These methods should generally attempt to abstract over the concrete representation of that state so that the actor can upgrade/migrate without friction of being locked in to an overly transparent API. There'll sometimes be a cost-vs-abstraction trade-off there. The built-in actors do not set a good example here. We cheated because the Go code defining their state and its interpretation could be directly executed by Go node implementations. Going forward, they will need to implement new read-only methods in order for any other contract to get any information (I have a plan for defining them). User-programmed actors must expose such methods. The FVM will not expose one actor's state to another actor (there is no We don't need any special VM support here. Any actor methods can be invoked locally, via the VM, without paying a gas fee to the chain. In on-chain transactions, they can be invoked normally (e.g. following a calling convention like #382). This will involve the necessary gas cost of loading the state, plus the cost of an internal message invocation. This latter cost could be avoided if we allowed actors direct access to each other's state, but I don't think anyone's a big advocate of that. |
Beta Was this translation helpful? Give feedback.
-
One idea that @Kubuxu and I have discussed is the possibility of an annotation for explicit view-only methods that can only be invoked locally, and cannot be invoked during a transaction. The goal would be to separate an actor's transaction API (callable by other contracts) from introspection methods for UIs, block explorers, etc. These methods might use the same calling convention for simplicity, but would not be reachable within an on-chain transaction context. This would require some support from the VM, with some environmental parameter indicating whether the call was in a transaction. We didn't pursue it too much. The main reason is that both kinds of APIs are still exposed to third parties who might reasonably expect that API not to change. Changing the code for a view-only method would still change the actor's WASM code, require an on-chain upgrade etc. And changing the API would break off-chain composability, like with third-party UIs, explorers etc. Maybe that's not quite as bad as breaking on-chain compatibility, but still undesirable. So we concluded that since the methods are a strong commitment in either case, we may as well have only one way to do it and have those methods invocable on-chain too. These compatibility concerns all lead towards very careful thought required about actor APIs to be exposed. FYI @raulk in case this is interesting. |
Beta Was this translation helpful? Give feedback.
-
@anorth I think solving this in an elegant, future-proof manner is on the critical path of M2 (rather: making M2 useful). A few thoughts:
|
Beta Was this translation helpful? Give feedback.
-
FWIW, I just ran into this (filed here) and skimmed over above discussion. Not sure, it seems there might be some confusion in what the EVM provides. This whole thing runs under the name "state mutability" in the EVM, and under "function modifiers" in Solidity. There are 2 modifiers for functions:
Apps calling into Web3 gateways to EVM nodes can do so without paying any gas, and without on-chain signed transactions. A Web3 gateway may of course control even read-access (eg Infura or Alchemy, two popular gateway/node providers allow only a certain volume of free requests. "free" for the app vendor that is) A contract function that does modify (contract) on-chain state must be fully signed, submitted on-chain and paid for execution in gas. Such an "external transaction" (the outermost call to a contract function submitted as a user signed transaction) can call other contract functions. Any gas needed is aggregated along the call stack. BUT: if the outermost transaction is "read only", it can also itself only call other contracts read-only functions, and hence the whole call is free and can run without an on-chain transaction. Web3 applications regularily call read-only functions (via a Web3 gateway), might call graph indexes, on-chain oracles etc all without the user having to sign anything. Long story short: not supporting view/pure modifiers for actor functions in some way would be a huge IMO .. Thinking about options, here are a couple of ideas:
|
Beta Was this translation helpful? Give feedback.
-
thanks for your reply! ok, I see.
yes, fwiw, I dug out the two web3 entry points used by apps
comments are from web3 spec and from geth sources: https://github.com/ethereum/execution-apis
ok, so it is still running, but state changes done by the call are on a per-call state copy thrown away? there might be a small difference still in semantics: "not reflected on-chain", but also returning an error to the invoker? when I call a state modifying (non-view/non-pure) function via I'm trying to understand the FVM code base, maybe you could give me some more hints? Calls are ultimately run in a container via the call manager https://github.com/filecoin-project/ref-fvm/blob/master/fvm/src/call_manager/default.rs#L197 which interestingly has a https://github.com/filecoin-project/ref-fvm/blob/master/fvm/src/executor/default.rs#L117 which hard-codes the flag to false? the executor is called from the kernel https://github.com/filecoin-project/ref-fvm/blob/master/fvm/src/kernel/default.rs#L349 which forwards and the kernel Is above about right? |
Beta Was this translation helpful? Give feedback.
-
Context
Smart contracts on other blockchain networks like Ethereum (and other EVM chains) frequently make use of "getters" or "view functions" - contract calls that cost no gas to invoke that return some data about the state of a smart contract. "Getters" are important because they reduce friction for creating composable chains of smart contracts (without forcing end users to pay gas to read data). Getters also make it easier for off-chain applications to make sense of an actor's internal state.
Current solutions
StateReadState
, passing theHead
CID toChainReadObj
, and then deserializing the params of state one by one.Downsides to current solution
StateReadState
to extract state data from an actor. First, it's overly complex to compute state that depends on the states of multiple underlying actors. Second, this strategy does not work for other FVM actors, so it's incomplete.This discussion is meant to propose various solutions for how to enable getters in the FVM. @raulk had already mentioned a few ideas on our last FVM early builders call.
Beta Was this translation helpful? Give feedback.
All reactions