As discussed here https://github.com/neo-project/neo/pull/784, I strongly argue that it would be better to have tx verification as turing-incomplete on Neo 3.0. This limitation would allow us to avoid the concept of system fees during tx transmission (only for contract execution, that could happen some time after block is actually relayed), and network fee could deterministically calculate and incorporate some other costs that affect tx verification, such as expensive elliptic curve verification, hashings, etc (currently, only tx size is considered). These new costs would be shipped as network fees, to compensate Consensus Nodes operations. It could be designed in such a way that very basic address types, such as singlesig could continue to be fee, but any other operation types would require a very small network fee for tx transmission (such as multisig).
So, network fee would continue to be a transmission + tx processing fee, while system fee would be the Turing-complete execution cost of an operation (with loops, dynamic invoke and everything else). network fee would affect the capability of a tx to be relayed, and system fee the capability of a contract execution to finish in a good state (non FAULT).
You can calculate the fee before execute the verification script, it is true. But I can make a very complex turing-incomplete script which will consume a lot of gas, and make it fail in the final step. The result is the verification fail, and my gas won't be spent. So we have to move the complex script into application trigger.
Ok, no gas will be spent, but this would fail directly on a network endpoint (some p2p node) not a consensus node. So, as long as nodes do not keep relaying failed tx, which is not of their interest, none of these would arrive to a consensus node (thus not affecting network performance).
These "attacks" (if performed systematically) are more similar to orchestrated denial of service on endpoints, and must be deal with using different techniques (IP blacklisting, etc), that @shargon and Red4Sec knows much better than me :) Under attacks, nodes could systematically reduce maximum accepted network cost, to control the situation and only pass on and evaluate small scripts (low network fee / lower processing risk).
If no loops are allowed, script execution will be proportional to its size, and only bigger scripts would require to be deployed on the chain (for repeated usage and storage usage, depending on network fee cost... so it's a user decision based on actual costs, not imposed). For example, even the biggest ones, like contract deploy with several bytes, are quite easy to evaluate on real-time, as we discussed already, since the integrity of the script is not confirmed by nodes (that now I fully agree with you).
In this sense, deploy cost (system fee) could be very cheap, and let network fee become the expensive part of a contract deploy (big tx size). This better reflects operation costs, in my opinion.
One practical example.
Verification Script:
VERIFY
PUSH
VERIFY
... (100x these)
PUSH
VERIFY
Current Price (network fee) on Neo 2.x: tx size
Current Price (system fee) on Neo 2.x: each verify has a fixed price, so lots of gas would be spent after verification and block persistence.
Proposed Price (network fee) on Neo 3.x: tx size + processing cost (similar to system fee, but calculated deterministically based on operations)
Proposed Price (system fee) on Neo 3.x: two options. (1) user would pay these costs again, double. (2) these wouldn't cost system fee. User would only pay system fees for deployed contracts (after APPCALL), which is not allowed on Turing-incomplete mode (on verification).
From a p2p/rpc node perspective: tx A arrives with expected 100 verifications, costing 100 GAS, and expected processing time is 10 seconds (we could calculate this as well). My load is currently light, so I'll try to do it. Or, my load is too heavy already, I will reject it due to excessive load (and not even process it).
A very nice thing is that RPC endpoints could start receiving a bonification (tip) for their services, so they would take risks of verifying tx of higher values. If a costly tx fails, rpc could blacklist the input address to avoid future attacks. On p2p, if a node receives invalid tx from other, it could also.blacklist its peer for wasting its time.
One of the purposes of #784 is to make the size of the transaction essentially fixed. This way the wallet will be simple when calculating the fee. However, this is not the most important.
In fact, this discussion only makes sense depending on the real costs of network and system fee. The current scenario for 2.x is: low network fee and high system fee. If price to deploy is very cheap, and network fee is high, there would be little difference between the approaches. The only real difference is the "smart transaction" capability of the network, that could be reduced to a single ecc validation, in order to achieve higher tps. What are real applications for a verification phase, being it very limited, Turing-incomplete or Turing-complete? If focus is on asset transfer validation, a simple ecc verify will do the job for 99% of the cases indeed.
This is mandatory for #840, therefore I'm adding Neo3-preview1 tag and adding it to the NEO3 Milestone
@neo-project/core are we able to make this on time for this release?
Another related proposal: #1332
Agree with your proposal, @erikzhang. However, this Turing incomplete for verification is a powerfull, safe and flexible tool.