-
Notifications
You must be signed in to change notification settings - Fork 28
Overview
Stuart Farmer edited this page Aug 1, 2018
·
3 revisions
Seneca is a subset of Python designed for authoring smart contracts. It’s syntactically valid Python and it can be parsed by the standard CPython interpreter. Differences and limitations:
- We heavily restrict what libraries can be imported. The only allowed imports are:
- The Seneca smart contract user libs. This is a set of useful functions made available to smart contract authors. We intentionally omit non-deterministic functions like random number generation and
datetime.now(). - Other smart contracts already present on the blockchain. The current methodology for import is to remove these nodes from the AST before execution, run a custom module loader, and bind them to the execution namespace.
- The Seneca smart contract user libs. This is a set of useful functions made available to smart contract authors. We intentionally omit non-deterministic functions like random number generation and
- Many language constructs are disallowed, in fact only a small subset are permitted. This is enforced by parsing smart contracts, generating an AST and only allowing certain AST node types, see: https://github.com/Lamden/seneca/blob/master/seneca/seneca_internal/parser/basic_ast_whitelist.py
- Data access
- Sandboxed DB
- Seneca data storage modules are already signed in.
- All or nothing (i.e. atomic) execution. Any failure on the Python side backs out all data writes
- Etc. (TBD)
- Data storage sandboxing
- All execution in same process, not secure. (TODO)
- Database executer in same process as interpreter, not secure. (TODO)
TBA
Currently there’s one entrance point into the Seneca interpreter, the execute contract function: https://github.com/Lamden/seneca/blob/85a4d617a40fd2f6cc47ea317bee2ebdd24df321/seneca/execute_sc.py#L247,L344 execute_contract() is a convenience wrapper function around the recursive _execute_contract() function. When a smart contract is passed to _execute_contract(), which imports another smart contract as a dependency, the function calls itself with that dependency as an argument. The base case is a contract with no (smart contract) dependencies.
- global_run_data - this dictionary has metadata about the main smart contract (submitted by a user):
- Who submitted it (i.e. what public key)
- What the contract’s id is (determined by Cilantro)
- Important: If the executed contract imports other contracts, _execute_contract() will recurse and global_run_data will be passed to those dependencies unchanged.
- this_contract_run_data - this includes context about the current smart contract:
- Similar to global_run_data, for the main contract it will be the exact same data.
- If the executed contract imports other contracts, _execute_contract() will recurse and this_contract_run_data will have metadata for the dependency, it won’t be the same as global_run_data.
- contract_str
- A string containing the smart contract code.
- module_loader
- A function provided by Cilantro. It has one parameter, a contract_id and returns a pair: metadata about the contract and the contract’s code as a string.
- db_executer
- A wrapper around a mysql connection that lets Seneca execute queries
- is_main
- Already deprecated from the convenience wrapper function. This indicates whether the called contract is the main one (submitted by a user), and not a dependency of a main contract.
- Use the CPython parser to parse the contract_str and create an AST.
- Validate the AST against the AST node-type whitelist, throw exception if a disallowed language construct are present.
- Create a new empty module execution scope.
- Traverse the AST looking for imports:
- If a Seneca user lib import is found, load the Seneca module and attach it to the module execution.
- If the Seneca module is database related, inject database connection into module.
- If a smart contract import is found:
- Use the module loader to retrieve that smart contracts code and metadata.
- Recursively execute _execute_contract() with that contract and attach that contract’s exports to the upstream module’s execution scope.
- If the import is neither a seneca module or a smart contract, throw an exception.
- If a Seneca user lib import is found, load the Seneca module and attach it to the module execution.
- Remove import nodes from the AST
- Execute the AST with the constructed scope.
- This actually executes the contract.