Our new programming language is object-oriented. It includes contracts, which are like classes, which can have fields and transactions, which are analogous to functions. In addition, of the many variables or fields that reference objects, exactly one of them can own the object, as seen in diagram (a) below. An object can have any number of Unowned references, and, if the object is not Owned, it can have any number of Shared references (shown in (b) below). An object with Shared references can also have Unowned references, but not Owned ones.
Here is an example of a contract (a Wallet
) with an object (in this case a Money
) that has one Owned
reference:
contract Money {
}
contract Wallet {
Money@Owned m; // @Owned indicates that the reference m owns the object it refers to
transaction spendMoney() {
...
}
}
The compiler tracks ownership of each variable every time the variable is used. This information is part of the type of the variable. For example, the type of m
is Money@Owned
. Information about ownership is NOT available at runtime; it is only available during compilation.
Owned
.Shared
.Unowned
.When transactions return objects, the types must be annotated in the function header. For example:
transaction withdraw() returns Money@Owned {
// body not shown
}
When a reference is passed to a transaction as an argument, the transaction's declaration specifies initial and final ownership with >>
. For example:
transaction spend(Money@Owned >> Unowned m) { // m is Owned initially but must be Unowned at the end.
// implementation not shown
};
transaction testSpend(Money@Owned >> Unowned m) {
spend(m);
// m is now of type Money@Unowned due to specification on spend() declaration.
}
If a transaction expects an argument that is Unowned
, this means that the transaction cannot take ownership. As a result, it is safe to pass an Owned
reference as an argument to a transaction that expects an Unowned
argument. After the transaction returns, the caller still holds ownership.
If a transaction parameter lacks >>, then ownership will not change. For example, transaction test(Money@Owned m)
is equivalent to transaction test(Money@Owned >> Owned m)
. transaction foo(Money@Unowned m)
can accept a Money
reference with any ownership and the caller maintains whatever ownership it had initially when it called that transaction.
this
)Sometimes the ownership of this
needs to change in a transaction. That fact can be specified by adding this
as the first argument in the transaction declaration. For example:
contract Money {
transaction discard(Money@Owned >> Unowned this) {
disown this;
}
}
contract Wallet {
transaction throwAwayMoney(Money @ Owned >> Unowned money) {
money.discard(); // 'this' argument is implicit; do not include it in transaction call.
}
}