Context
We mandated a new round of very large scale security audits that spanned multiple months to reinforce the security of Massa. The focus this time was on the Execution system (sequencing, transaction execution, VM and so on…).
The auditors found security details in the code that could be improved, which we did over multiple back-and-forths between our devs and the auditors. The fixes cumulated 10,000+ lines of code and were delivered partially in v2.5 and the rest was delivered in v3.0.
In the report we ended up in agreement with the auditors on almost all the 45 findings:
- either we agreed they were right and we fixed the issue, then they re-audited the fix and accepted it (resolved: 36/45)
- or we agreed with their findings and accepted that it is part of the design, added it to the documentation if necessary, and they agreed it was OK (acknowledged: 6/45)
- or we disagreed with their interpretation (declined: 3/45) <== this is what we are talking about here
The disagreements are the following:
- https://skynet.certik.com/projects/massa?auditId=Massa%20Labs%20-%20L1&findingId=SIG-01#code-security => they complained that we don’t check for signature malleability in batched signature verification, to which we responded that Massa was designed from the ground up so that malleability does not cause any security issues. They demoted the finding from “critical” to “informational” after our response, but did not agree with removing it.
- https://skynet.certik.com/projects/massa?auditId=Massa%20Labs%20-%20L1&findingId=SRX-04#code-security => they were surprised by the fact that in Massa, a smart contract doing something is fully responsible for paying gas, storage etc… for whatever it does, including contract deployment. This is due to the design choices of Massa: each smart contract is meant to be fully autonomous, so it should pay its own stuff. Because of this, the audit team warned that fees might be “misrouted”: if smart contracts are not designed properly to require the caller or another source to cover for their expenses, they might end up using locked funds. This is a documented design choice of Massa and a necessary condition for autonomous smart contract execution, hence our disagreement. They demoted it to “medium”.
- https://skynet.certik.com/projects/massa?auditId=Massa%20Labs%20-%20L1&findingId=A4D-02#code-security : here they say that the lack of Nonce in Massa transactions is a major issue, and we disagree. <== this is what this post is about
How it works in Ethereum
In Ethereum, nodes store the whole history of executed transactions and their execution conditions. Blocks are not parallel but sequential and are not very frequent, so this list does not grow quickly, and node storage space is expected to increase without cap over time as the history grows.
When a block is received by a node, its validity is checked, which includes checking that its hash does not belong to the full list of already-executed transactions. It also checks that the “nonce number” if present is consecutive to the previously registered one. This is useful for transaction sequencing from a single account. It also has another feature: replacing a transaction before it is included by another one (albeit there is no mathematical guarantee that this would happen).
Ethereum suffers from MEV (Miner Extractable Value) because actors in the network can observe that a transaction will happen before it is included in a block, and quickly send a transaction with a higher fee to be executed before it, in order to change the market conditions and make a profit (typically something called MEV Sandwich Attacks).
Why Massa has no nonces
Massa on the other hand is designed to process orders of magnitude more transactions per second than Ethereum (about 10,000 tx/s !), all while maintaining node hardware requirements constant and reasonable. To achieve this, blocks are very frequent in Massa (2 blocks per second), and 32 blocks are being produced and propagating simultaneously in the network at any given moment. This has two consequences:
- if we required nodes to store the history of executed transactions and their nonces, it would quickly occupy too much space and break the low hardware requirements, hence break decentralization
- Massa is designed with parallelization in mind: there are no guarantees in the order of execution of two given transactions. Adding a nonce would break this pattern.
In Massa, transactions contain an expiry period (see Detailed operation format and execution sequence | Massa Documentation ) after which if the transaction is included in a block it will be ignored. In a similar fashion, it can’t be executed if included earlier than 10 periods before that expiry period.
Before executing a transaction, we check that:
- it is not part of the temporary rolling cache of non-expired executed transactions (transactions are removed from that list once they are expired because they can’t be included anymore anyway)
- it is in its validity range in terms of periods (see above)
That way, Massa retains gains a lot in hardware strain and scalability at the same time, which is necessary to achieve such scaling without crippling decentralization.
Security considerations
In Ethereum, block producers are free to order transactions the way they want in their block.
Only one constraint applies: they need to respect the nonces when ordering transactions coming from a single given account. But transactions coming from different accounts are arbitrarily ordered by the node runners. This opens the well known MEV opportunities happening currently in essentially all L1 blockchains.
In Massa, block producers are also free to order transactions coming from different accounts within their block, but they are also free to order transactions coming from the same account within their block. Massa is massively parallel: there is no guarantee of ordering of concurrently emitted transactions.
We believe this is not a problem because:
- the issue is only block-wise and there are 2 blocks per second in Massa. So unless you are spamming more than 2 transactions per second that all need to follow a certain sequence, it should not be an issue
- if you are doing something sequence-critical, you should use a smart contract that ensures sequence numbering. This is much more powerful than nonces because it can ensure cross-account sequencing as well, and not just order the transactions coming from one given account.
Actually MEV might be harder in Massa than elsewhere because as a block is being produced, 31 other blocks are also being produced and propagating simultaneously, and MEV actors don’t necessarily know their contents in time.