-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Allow off-chain floating point operations for Wasm smart contracts #11367
Comments
We cannot support real non-deterministic operations in polkadot or parachain runtimes. |
@burdges The team that is requesting this feature is using the |
|
We do not necessarily even need restrictions then, merely that polkadot's wasm should not support floats. If some parachain team configured their wasm time to support floats, then their chain could create invalid blocks, in that polkadot would reject them, or possibly not even compile their PVF, but if they never do so then everything remains fine. They can do this in their own fork without us altering substrate probably. In other words, polkadot would make consensus exist for this parachain. You need restrictions only if they wish to run using their own internal finality, because floats would make their own nodes incapable of internal consensus. |
wasmtime is not relevant here. This is about contracts. |
I've no idea if contracts are implemented via wasmtime or not, but my point is: We'll always enforce determinism in polkadot, so it does not matter if a pure parachain does not internally enforce determinism since polkadot will enforce determinism for the parachain. It only matters if the parachain wants its own separate consensus. |
@burdges Determinism is not always required, especially in our use case. Please allow me to add some context here. Phala Network adopts ink! (with Without the consensus requirement, we can do a lot of interesting things with stateless functions like heavy computation and internet access. For example, recently we are trying to build an oracle-like service by sending http request to fetch some data, and parse the json from the response. You can find a similar demo here. However the json standard includes float point. As long as we introduce any json parser, it's inevitable to trigger the float point error in So here I think the solution could be, we postpone the check to runtime. The wasm code is instrumented. So we can also color the basic blocks with float point, and mark the execution as "non-deterministic" when it reaches the float point operations. In this way, we don't affect the determinism on-chain, but more use cases can be supported. |
Is this really required? The easiest solution would be to mark a whole contract as off-chain only when it contains such instructions. Do you expect that parts (non float) of the contract will be executed on-chain? |
As an aside, how do we enforce no floats in polkadot? I'd always assumed we just disabled them entirely in wasmtime, so that polakdot could not even compile wasm with float instructions, which sounds safest.. |
Yes, Fat Contract in the first place works as a rollup solution. Although running off-chain, it still has the contract storage and reacts to the on-chain transactions where the storage can be modified. We call them mutable calls. They has the same effect as the vanilla ink!. On the other hand, it supports complex and async computation in queries (immutable calls), like what we have described above. Immutable calls cannot live without mutable calls, because we always need some minimal strong consistent storage for config and authentication. For example, in a typical oracle use case, we may generate a signed payload with some off-chain data we got from a http request in a immutable call, and then verify it on the blockchain. In this case, we need to generate a key pair in the storage when initializing the contract, and use the private key to sign messages. If we completely disable mutable calls because of the float point introduced by a json parser, there's no chance store any key pair in the contract storage. |
Again, we are not talking about wastime/polkadot here. It is about contracts. They are not run in the same instance if that is what you are thinking. |
@h4x3rotab Okay got it. It needs to be a dynamic thing then: The interpreter needs to error out when hitting such an instruction. This won't work with a JIT compiler though. At least not how the "JIT" compilers for wasm work. They are not dynamically recompiling but just compiling the whole module at once. So we wouldn't know when hitting such an instruction. So this just got a whole lot more complicated: Adding specific requirements to wasm execution engines is something that we try to avoid in order to not be locked in with one. |
Can't you split this up in two contracts? One has the storage (A) and mutable calls but no floats and can be easily run on-chain. Then you have another one (B) which contains floats and is responsible for the calculation. In case of off-chain calculation B would query all the data from A by calling it and then does its calculation on it. It doesn't matter if a lot of data is dumped from A to B because it only happens off-chain. This would remove the dynamic requirement and makes this easily implementable. |
Brilliant solution! Let's do it in this way. |
The solution is acceptable to us. Do I understand it correctly? |
Yes you got that correctly. It is a good point: You can't create contract B cause you cannot run the constructor on-chain of a code that is flagged as in-deterministic. There is a solution for that and I think this is even more elegant: You don't actually need a contract B. You only have a code hash B which is flagged as nondeterministic. What you would do is to use |
Perfect! |
Replaced by #12063 |
Floating point is common used in practice, in a json parser for example. Currently, the pallets-contrat forbid floating point types in the contract codes in order to be deterministic.
As discussed in the Element group, we may add a parameter like
bare_call(ALLOW_INDETERMINISM )
to allow non-deterministic operations like floats in immutable context.The text was updated successfully, but these errors were encountered: