-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Summary or problem description
This is a proposal to support Stateless (Turing Complete) Contracts on Neo (or N4) and a brief history since N2 on how this is important and doable on Neo. This requires a Pre and Post verification script for contract and also a single "Contract State" storage hash, managed by the contract itself.
Do you have any solution you want to propose?
Neo originally adopted bitcoin UTXO model, without any Turing-Complete contracts (on antshares), but already with Bitcoin-like Script Verification (loopless, gasless and Stateless). Then the Storage was added together with Turing-Complete contracts, on early N2, and suddently some limits were reached, specially for outbound transactions (UTXO outputs), thus requiring a VerificationR script.
This approach ended up with problems related to the previously deployed contracts, as seen here:
- VerificationR - Feature manifest and permission system for NeoContract #287
- prefixed methods - Create a prefix reserved for system methods #1097
- From 2019 - "We can't add new system methods, like VerificationR or other, because we can't say if this is going to break current contracts."
This is not completely the focus of this issue, just part of the story, so I recommend digging deeper in another future issue (#4382). But this has impacts on how a Contract can be Safely Upgraded without requiring Hard Forks.
After contracts became stateful, at that time not fully/strictly controlled by a global state hash, eventually some states could be changed, in face of bugs and unexpected upgrades.
For this reason, an approach was proposed in 2019 by registering the Contract State AS A WITNESS, during Transaction processing, ensuring that such registered state could never change. This was called Solid State Transfers: #2013 - solid state transfers
The risk could be then that a previously valid transaction, that was put on block, would then become Invalid, generating more risks due to Storage Read operations on a Witness Verification.
Related payload:
- transaction - https://github.com/neo-project/neo/blob/master/src/Neo/Network/P2P/Payloads/Transaction.cs
- payload witness - witness - https://github.com/neo-project/neo/blob/master/src/Neo/Network/P2P/Payloads/Witness.cs
For this reason, the Verification model became less and less powerful, to ensure Transaction ordering, but also preventing non-deterministic accesses during Verification. See this comment from @erikzhang in 2019: #332 (comment)
Using storage in verification can fork the network. In addition, NEO 3.0 will disable most of the SYSCALLs to speed up the verification progress.
From the global programming community, not only for blockchain, many are evolving for a Contract-based programming, so I reference here the proposal for C++26 Contracts. These are basically two conditions put on a function, as pre and post conditions, that will allow functions to be much safer and also to allow greater verifiability and security of C++ codes. I strongly believe that this the case for blockchain, maybe EVEN MORE!
Example: A users sends a transaction for X token transfer, so she EXPECTS that her balance updates -X, otherwise, something went wrong. Or she can EXPECT to have AT LEAST some tokens, which is another more flexible way to allow PARALLEL TRANSACTIONS (just like in old UTXO model and also in Account model).
To make a Smart Contract stateless on general, but verifiable, we can allow it to keep and maintain, by itself, a SINGLE UNIFIED STATE HASH, that can be merged with other state hashes and put in a global chain hash, example, by some MPT. One thing is for blockchain to maintain storage state, and another is for contract to do it by itself. This way, storage can become effectively ZERO for the contract, putting the burden on Users to submit a correct STORAGE PROOF related to the contract hash, as a Pre-Conditiion Verification (just like a current Witness, but tied to some specific deployed Contract). In the same way, a user can provide a Post-Condition, however it is Debatable if such Post condition should be verified Immediately, during P2P and Transaction ordering (which is hard!), so most likely only executed and checked on NEXT BLOCK (so it becomes 2-block finality for Post-Condition, with the ability to definitely Fault or Unfault such execution). P2P nodes can easily perform such checks, as soon as some block is signed and distributed, so next block Pre-Condition should consider Post-Condition results.
This allows the creation of some sort of "Stateless Storage" or even a "Storageless Storage", that only "shows" during execution the data that is made available by the user as a Pre-Condition, and all the rest of the data is Unreadable and possibily unavailable. Note that this requires the contract to have a Strict management of its storage format, specially for Fungible and Non-fungible token standards, perhaps requiring a new NEP to ensure that wallets can correctly recognize and provide correct storage format and storage proofs.
This is somehow related to discussions on Volatile Storages:
#1514 - #332 - volatile storages
However, this kind of Stateless Storage here is merely a programming trick to avoid storing data onchain, and allowing thirdparty (and even Smart Contract owners/managers) to strongly contribute to blockchain scalability by providing easy storage data and storage proofs off-chain, without breaking the trust on the contract correctness.
At this point, I quote our great magician @erikzhang from 2018, that foresaw this application using NeoFS or even IPFS:
#332 (comment)
Maybe we can store the hash in the blockchain and store the data in NeoFS?
So yes Erik, just would be amazing, and now we can do it!
That said, this will also allow some very interesting use cases where some contract migration Freezes its State and then Returns in a DIFFERENT STATE.... in this case, unless thirdparty provides such new state, users won't be able to operate correctly. But there are use-cases related to Privacy and Isormorphic chain computing that would allow some contract state to be "hidden" by a trusted thirdparty, or even government agencies, thus enlarging A LOT the number of possible blockchain applications that require privacy on turing-complete and not complex/expensive ZK proofs.
This also allows easy chain interoperabily and can boost Neo capability for evolution in blockchain scene.
Finally, contract can DISAGREE on its own state, after some Bug or Attack, and Rewrite its own history, without requiring to Break or Fork the Blockchain. Blockchain becomes more agnostic to what to contract does, focusing more on execution and transaction ordering.
ON PRACTICE, THIS REQUIRES:
- a field (or list of such fields) named PreVerification on Transaction, holding a Contract Hash, a ScriptHash (for the precondition script) and a ByteScript (or NEF)
- a field (or list of such fields) named PostVerification on Transaction, holding a Contract Hash, a ScriptHash (for the postcondition script) and a ByteScript (or NEF)
- Perhaps on block, to prevent post-condition races and make transaction run fully checked, a field that Faults or Accepts each previous transactions in past block (just a Bit for each transaction, 0/1). Or just let it run unchecked, no new Block fields are needed, and we keep 1-block finality, but Post-condition will need to be executed right after the script, and revert execution if it fails (on Application, not Verification)
This would require some new interops like Storage.VerifiedGet and Storage.VerifiedPut... and perhaps, reviving some old but still important demand, some Storage.Add or Storage.VerifiedAdd (#814).
I hope this is convicing enough to get into the roadmap for n4 - #4198
Where in the software does this update applies to?
- Compiler - nothing
- Consensus - Perhaps the 2-block finality operations for Post-Condition
- CLI - Maybe computer the proofs for the user to operate in such Stateless NEP-17 tokens.
- Plugins - Maybe to store some alternative storages and storage proofs for the user
- Ledger - Transaction Payload
- Network Policy - nothing
- P2P (TCP) - Transaction Payload
- RPC (HTTP) - Maybe to store some alternative storages and storage proofs for the user
- SDK - More for SDK just a few new Interops.
- VM - nothing
- Other: