You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After a conversation with @marekkirejczyk I have decided to revisit code from the early days of Mars to try to remember why Features were introduces, what benefits they bring over promises and how a safe migration away from them could be made.
Some history
When Mars was first envisioned we thought about it as Terraform but for smart contracts. A key idea from that time was to copy the Write, Plan, Apply model - Mars scripts would tell you what they were going to do before sending a single transaction.
In order to achieve this execution plan we couldn't rely on promises, because the concrete steps taken would only be known once the code is run, and the original code didn't have a dry run option.
Benefits of futures
While Futures provide little benefit in terms of things that are possible (because once dry run was implemented you could freely run your code without actually sending any mainnet transactions) their restrictiveness circumvents some possible issues with a Promise approach:
It is hard to implement a for loop based on the value returned from a smart contract, meaning that the scripts are easier to reason about. All contracts deployed in a for loop would require unique names otherwise only one contract would be deployed which is non-obvious.
It is impossible to run code concurrently, which means that we are guaranteed that a transaction finishes execution before sending another one. While this means that the script is slow it also means that the script is safe from problems with nonces or failed transactions which were required to succeed before proceeding.
Because all the code is synchronous all the code can use a shared global context and it can easily be enforced that code has to reside inside a deploy function. Otherwise one might use Mars functions outside of the available context and get unexpected results, because Mars would think they are part of another script run: e.g:
When using JS one can achieve a runtime pseudo type safety, because the code produces the execution plan which then is later executed, meaning that if the execution plan cannot be produced then no execution occurs. This in turn prevents sending a few transactions only to discover that the second part of the script actually throws a TypeError.
Mitigations
For loop
The below code will actually only result in a single code deployment, because subsequent calls to contract see that a Token contract with such a name was already deployed. This is unintuitive and can confuse script authors.
for (let i = 0; i < 5; i++) {
await contract(Token)
}
// or
await contract(Token)
await contract(Token)
I propose that the contract function checks whether or not a particular name has been queried for previously during script execution and throws an error or produces a warning that explains that the user should provide a different name for the contract.
Promise.all
The below code introduces a potential problem where the transactions execute in parallel with nonces and reverts breaking the script logic.
To mitigate this a locking system has to be implemented which detects that an execution is in progress and throws an error explaining to the user that they cannot run code concurrently in Mars.
Global context
With Futures Mars is able to store some parameters in a global context that is only used during the execution planning phase to pass parameters which makes it short lived and relatively safe. With promises the global context would live longer and checks preventing re-initializing the context mid execution have to be put in place.
Execution safety
It would be ideal for Mars to detect if the code of the script changed and refuse to execute without doing a dry run first. This would serve a similar purpose as the plan phase with Futures, evaluating the script in its entirety before sending any real transactions.
The text was updated successfully, but these errors were encountered:
Thanks a lot @sz-piotr for input from your memories. For the moment being, I'm coordinating development into Mars. Could you see my ADR #82 that considers 1) modularity improvement and 2) future of the Future?
Meanwhile I'm going to think thoroughly your input.
There's also a hot new feature that just landed here: multisig support. See #85 . Maybe it covers some of the workflows that Futures aimed to support too.
After a conversation with @marekkirejczyk I have decided to revisit code from the early days of Mars to try to remember why Features were introduces, what benefits they bring over promises and how a safe migration away from them could be made.
Some history
When Mars was first envisioned we thought about it as Terraform but for smart contracts. A key idea from that time was to copy the Write, Plan, Apply model - Mars scripts would tell you what they were going to do before sending a single transaction.
In order to achieve this execution plan we couldn't rely on promises, because the concrete steps taken would only be known once the code is run, and the original code didn't have a dry run option.
Benefits of futures
While Futures provide little benefit in terms of things that are possible (because once dry run was implemented you could freely run your code without actually sending any mainnet transactions) their restrictiveness circumvents some possible issues with a Promise approach:
deploy
function. Otherwise one might use Mars functions outside of the available context and get unexpected results, because Mars would think they are part of another script run: e.g:Mitigations
For loop
The below code will actually only result in a single code deployment, because subsequent calls to
contract
see that aToken
contract with such a name was already deployed. This is unintuitive and can confuse script authors.I propose that the
contract
function checks whether or not a particular name has been queried for previously during script execution and throws an error or produces a warning that explains that the user should provide a different name for the contract.Promise.all
The below code introduces a potential problem where the transactions execute in parallel with nonces and reverts breaking the script logic.
To mitigate this a locking system has to be implemented which detects that an execution is in progress and throws an error explaining to the user that they cannot run code concurrently in Mars.
Global context
With Futures Mars is able to store some parameters in a global context that is only used during the execution planning phase to pass parameters which makes it short lived and relatively safe. With promises the global context would live longer and checks preventing re-initializing the context mid execution have to be put in place.
Execution safety
It would be ideal for Mars to detect if the code of the script changed and refuse to execute without doing a dry run first. This would serve a similar purpose as the plan phase with Futures, evaluating the script in its entirety before sending any real transactions.
The text was updated successfully, but these errors were encountered: