Seed - Dev. Discussion - Provable Execution With Function Hashing In JavaScript
The Seed project will require, effectively, it’s own “virtual machine” like the EVM in Ethereum that would allow for our modules to have code execute. However, Seed has a few notable differences and design philosophies than Ethereum and similar competitors. One primary difference in mindset is, in Seed, we would prefer provable execution in a trusted language rather than build a new layer abstracting away from our language of choice. Essentially, why build our own language, compiler and virtual machine if we can achieve the same results with use using our shared language natively. Today, we’re going to look at how we can achieve that.
Function Hashing & Checksum
One interesting way to prove which exactly pieces of code got executed is to hash the raw data of the function that a module developer wrote. In Seed this can be very easily done as it is created in JavaScript. In JavaScript, everything is an object, including individual functions. All objects can be turned into a JSON equivalent, including functions. These JSON equivalents, being strings, can simply be hashed. This allows for us to pass around a lean hash of the function executed, rather than the entire executable function, while still guaranteeing that the functions are completely identical. This will be required to prove that no client had any code injected into the functions, because any injected or modified code would result in a different hash.
To limit storing, we will use the checksum of hashes. What this mean is, if a hash is ABCDEFGHIJ, we could take a four byte checksum from the start, which would be ABCD in this example. This is essentially storing a small portion of the hash which future users who derive their own hashes from their own content can compare to in order to prove their code would generate the same small checksum.
Isolating Preconditions & Effects
In order for users to agree, in the Seed blockchain users do not need to fully agree on what the inputs are into functions. This is because at any point a user may have limited knowledge through the DAG on certain transactions. Rather than desiring to prove users agree on inputs, and therefore trust all inputs will give the same deterministic output, the Seed system takes a backwards approach. We require users to agree on the outputs, because matching outputs means that the inputs did not conflict, allowing users to have limited knowledge for inputs.
In order to get a proper consensus, users would have to pass not just their function hash for the function they executed, but also pair with it the isolated outputs and effects this execution caused on the ledger. For example, if a function who’s checksum was ABCD effected the world by making the user Bill lose ten dollars, than the paired data to be signed would be similar to [ABCD,Bill.Dollars+=10]
Provable Consent
The final portion required for our provable execution is the users consent. Consent in cryptography stems from the public key encryption signatures, which we discuss in more detail here. The transaction signature created by a user will sign the hashed data of the entire transaction, and because these components will be part of a transactions data, a users signature on a transaction proves they are consenting to a specific function checksum and effect.
Dependency Injection
In order to give module developers power over reading the ledger, creating other transactions and interacting with the network in a limited fashion, all functions will allow for dependency injection. For example, should a function require access to the ledger, it should be able to request for “Ledger” in the parameters and receive an object which the function can safely read from or manipulate as it pleases. This style of coding is to allow the developers to have limited power over the system while still accessing what they may require.
Malicious Functions
We spoke about function validity to avoid code injection, as well as dependency injection as a way for the system to supply safe versions of objects to functions. The final note which we need to officially address is malicious functions. In a the Ethereum Virtual Machine, if a forever loop is present in the code, it will drain the user of their gas until it runs out, fails and executes early. We do not truly have the power when giving users the ability to write freely in JavaScript.
One way we can try and solve these issues is by adding requirements the code must pass in order to be valid. For example, our system could deem any function with “while(true)” in it to be malicious. Similarily, any function which attempts to access elements such as DOM could be rejected. These would require parsing of functions before hashing, while the data is still in tact, to prove they are not malicious in nature.
Conclusion
Overall, this is a very interesting approach which could end up giving users very modular, efficient code created without requiring a separate layer for a virtual machine. However, there are security risks. Certain risks have been addressed above, however other risks are certainly present. Further research into JavaScript vulnerabilities, malicious functions and provable execution will be required.
Thanks for reading! As always, if you enjoyed this development discussion, an upvote or follow are always appreciated!