Skip to content
On this page

Contracts

Contracts in SCIF contain persistent state variables and methods that perform computations and operations.

Structure of a contract

The declaration of a contract starts by specifying its name and label, followed by its body contained in a braces pair.

The body of a contract may contain the following:

  • state variable declarations
  • exception definitions
  • local trust relationship specifications
  • constructor definitions
  • method definitions.

The following example shows a basic contract.

scif
contract ContractName[this] {
    // state variables
    map(address, uint) balances;
    final address owner;

    // exceptions
    exception TransferFailure(address from, address to, uint amount);

    // local trust relationships
    localtrust {
        principal high, low;
        high => low;
        this => high;
        owner => this;
    }

    // constructors
    constructor(address owner) {
        this.owner = owner;
    }

    // methods
    @public 
    void transfer(address to, uint amount) throws (TransferFailure) {
        if (balances[msg.sender] >= amount) {
            balances[msg.sender] = balance[msg.sender] - amount;
            balances[to] = balance[to] + amount;
        } else {
            throw TransferFailure(msg.sender, to, amount);
        }
    }
}

State variables

State variables are contract-level variables. They are stored instorage of a contract and thus are persistent. A state variable can be declared as final, similar to Java's keyword final, meaning that this variable can only be initialized once in constructors and cannot be reassigned later.

Exceptions

Exceptions are defined by specifying the name and the arguments of the exception. Programmers can use try/catch clause to catch exceptions and use throw statement to throw them.

An exception must be caught in a method or be specified in the method's signature.

There are also some built-in exceptions:

  • OutOfGasException: a method call throws this exception when the current transaction runs out of gas.
  • RevertException: revert statement reverts all changes in current transaction and throw this exception.

Local trust relationships

Programmers can declare principals that are not addresses and trust assumptions between principals in localtrust clause of a contract.

By specifying A => B, the trust relationship that principal B trusts principal A is put into the local assumptions when typechecking. By specifying A == B, the trust relationship that principal B and principal A are mutually trusted is put into the local assumptions when typechecking.

Note that it is the programmers' responsibility to ensure the correctness of these assumptions. Failure to do so might result in security vulnerabilities.

Methods

Programmers declare a method by specifying its return type, name, arguments, and its body.

In addition, methods can be annotated with decorators to specify their properties:

  • public: this method is an entry point and can be called by external accounts.
  • private: this method can only be called by other methods inside this contract.
  • payable: when called, the caller can send ether associated with this call.
  • override: meaning that this method is overriding an existing method of the super contract.

Method labels

Labeling a method is more complicated than labeling a variable.

A method is labeled as {l1 -> l2; l3} where l1 represents the requirement of the caller's integrity level, l2 represents the control flow integrity level at the beginning of the method, and l3 represents the lock level this method respects.

Inheritance

SCIF supports inheritance when defining a contract by using the keyword extends:

scif
contract B extends A {
    ...
    @override
    @public 
    void foo() {
        ...
    }
}

Like in Java, contract B will inherit all state variables, exceptions, and methods from A. B can also override A's method by decorator override.

Interface