No Classes Allowed!
Contracting maintains a strict 'no classes' model. This forces you as the developer to create more procedural code that is explicit and completely self-contained. Contracts must be easy to read and understand for validity. Instead of thinking of your code in classes, think of each contract as a 'module' that exposes certain methods to it's users.
class keywords will fail your contract on submission. Even if you try to use classes for object oriented code, you will have to find another way to express your structures.
class Car: def __init__(self, make, model): self.make = make self.model = model
This is illegal and will fail. Instead, think of each smart contracting having access to storage space. To access this storage space, you use a Hash or a Variable object. Hash objects are 'auto-vivifying' which just means they are dynamic. You can access and store data into certain keys by using the following pattern:
cars = Hash() # First key is the primary key, or the name of the object cars['balthasar', 'make'] = 'Ford' cars['balthasar', 'model'] = 'Contour'
Read more about storage in the Storage section.
Certain builtins such as
compile are obviously dangerous. We do not want to allow any arbitrary execution of code.
Here is a full list of Python3.6 builtin methods versus the ones we allow in Contracting. NOTE: All exceptions except the base Exception class are removed from Contracting.
|Built-Ins||Python3.6||Contracting||Reason for Restriction|
|callable()||✓||✘||methods are not passed as objects in Contracting.|
|classmethod()||✓||✘||Classes are disabled in Contracting.|
|compile()||✓||✘||Arbitrary code execution is a high security risk.|
|complex()||✓||✘||Complex numbers are potentially non-deterministic. This is a consensus failure risk.|
|delattr()||✓||✘||Arbitrary removal of Python attributes could allow unauthorized access to private objects and methods.|
|dir()||✓||✘||Allows exploration path into security exploit development.|
|enumerate()||✓||✘||Potentially safe. Evaluating to make sure.|
|eval()||✓||✘||Arbitrary code execution is a high security risk.|
|exec()||✓||✘||Arbitrary code execution is a high security risk.|
|float()||✓||✘||Floating point precision is non-deterministic. This is a consensus failure risk.|
|getattr()||✓||✘||Arbitrary access to attributes could allow private method execution.|
|globals()||✓||✘||Access to global scope methods allows modification of private methods and direct storage mechanisms.|
|hasattr()||✓||✘||Allows exploration path into security exploit development.|
|hash()||✓||✘||Potentially non-deterministic outcomes. Consensus failure risk.|
|id()||✓||✘||Potentially non-deterministic outcomes. Consensus failure risk.|
|input()||✓||✘||User input not supported.|
|iter()||✓||✘||Potential mutation of objects that are only supposed to be interfaced with through particular methods.|
|memoryview()||✓||✘||Potentially non-deterministic outcomes. Consensus failure risk.|
|open()||✓||✘||File I/O not supported.|
|property()||✓||✘||Property creation not supported because classes are disabled.|
|repr()||✓||✘||Unnecessary and non-deterministic due to memory address as output of this method. This is a consensus failure risk.|
|setattr()||✓||✘||Arbitrary setting and overwriting of Python attributes has storage corruption and private method access implications.|
|staticmethod()||✓||✘||Static methods are not supported because classes are disabled.|
|super()||✓||✘||Super is not supported because classes are disabled|
|vars()||✓||✘||Allows exploration path into security exploit development.|
Illegal AST Nodes
Similarly, some of the AST (abstract syntax tree) nodes that make up deeper levels of the Python syntax are not allowed. Mainly, the nodes around the async/await features are restricted.
|AST Node||Reason for Restriction|
|ast.AsyncFor||All async code is invalid in Contracting.|
|ast.AsyncmethodDef||All async code is invalid in Contracting.|
|ast.AsyncWith||All async code is invalid in Contracting.|
|ast.AugLoad||AST Node never used in current CPython implementation.|
|ast.AugStore||AST Node never used in current CPython implementation.|
|ast.Await||All async code is invalid in Contracting.|
|ast.ClassDef||Classes are disabled in Contracting.|
|ast.Ellipsis||Ellipsis should not be defined in a smart contract. They may be an effect of one.|
|ast.GeneratorExp||Generators hold state that is incompatible with Contracting's model.|
|ast.Global||Scope modification could have security implications.|
|ast.Interactive||Only available in Python interpreters. Potential security risk.|
|ast.MatMult||New AST feature. Not yet widely adopted. Potential security risk.|
|ast.Nonlocal||Scope modification could have security implications.|
|ast.Suite||Similar to ast.Interactive|
|ast.Yield||Generator related code is not compatible with Contracting.|
|ast.YieldFrom||Generator related code is not compatible with Contracting.|
The linter will check for several violations that will fail your smart contract automatically. Here is a list of the current violations and examples of code that will cause them.
S1- Illegal contracting syntax type used
Thrown when an AST type that is not allowed is visited by the linter.
def bad(): 2 @ 2 # ast.MatMul code
S2- Illicit use of '_' before variable
_ is used for gating certain functionality. Using it as a prefix to any variable will cause failure
def bad_var(): _balances = Hash()
S3- Illicit use of Nested imports
import keywords found inside of methods, loops, etc. will fail.
def bad_import(): import this_wont_fail @construct def seed(): import this_will
S4- ImportFrom compilation nodes not yet supported
Selective importing is not supported and will fail contracts.
def bad_import(): from token import send
S5- Contract not found in lib
Not currently used.
S6- Illicit use of classes
Classes are not supported in Contracting and their keywords will fail your contract.
def bad_classes(): class Car: def __init__(self, make, model): self.make = make self.model = model
S7- Illicit use of Async methods
Any async related code will fail the contract.
def bad_async(): async def fail_me(): pass
S8- Invalid decorator used
@construct are the only two decorators allowed in Contracting.
def bad_decorator(): v = Variable() @construct def seed(): v.set(100) @export def get_v(): return v.get() @unknown def this_will_fail(): pass
S9- Multiple use of constructors detected
Only a single
@construct can be included in a contract.
def bad_construct(): v = Variable() @construct def seed(): v.set(123) @construct def seed_2(): v.set(999)
S10- Illicit use of multiple decorators
Stacking decorators is not allowed.
def bad_construct(): v = Variable() @export @construct def seed(): v.set(777)
S11- Illicit keyword overloading for ORM assignments
ORM arguments are injected into the __init__ method on runtime. Messing with these will fail your contract.
def bad_var(): v = Variable(contract='token') w = Variable(driver=None) x = Variable(another_kwarg='this will fail') @export def set(): v.set(777) w.set(999) x.set(123)
S12- Multiple targets to ORM definition detected
Python allows multiple assignment. Trying to do a multiple assignment from an ORM object will fail your contract.
def bad_targets(): x, y = Hash() @export def set(): x['stu'] = 100 y['stu'] = 999
S13- No valid contracting decorator found
A contract without a single
@export decorator is invalid.
def bad_export(): @construct def seed(): pass
S14- Illegal use of a builtin
Referencing a builtin that is illegal will fail the contract.
def bad_builtin(): @export def credits(): return credits
S15- Reuse of ORM name definition in a method definition argument name
Reuse of any ORM names in any loops, methods, etc. will fail the contract.
def bad_var(): used_once = Variable() @export def override(): used_once = 123