Check stateful annotations
Functions must be annotated as `stateful` in order to - Update the contract state (using `put`) - Call `Chain.spend` or other primitive functions that cost tokens - Call an Oracle or AENS function that requires a signature - Make a remote call with a non-zero value - Construct a lambda calling a stateful function It does not need to be stateful to - Read the contract state - Call another contract with value=0, even when the remote function is stateful
This commit is contained in:
@@ -4,7 +4,7 @@ contract BasicAuth =
|
||||
|
||||
function init() = { nonce = 1, owner = Call.caller }
|
||||
|
||||
function authorize(n : int, s : signature) : bool =
|
||||
stateful function authorize(n : int, s : signature) : bool =
|
||||
require(n >= state.nonce, "Nonce too low")
|
||||
require(n =< state.nonce, "Nonce too high")
|
||||
put(state{ nonce = n + 1 })
|
||||
|
||||
@@ -3,7 +3,7 @@ contract BitcoinAuth =
|
||||
|
||||
function init(owner' : bytes(64)) = { nonce = 1, owner = owner' }
|
||||
|
||||
function authorize(n : int, s : signature) : bool =
|
||||
stateful function authorize(n : int, s : signature) : bool =
|
||||
require(n >= state.nonce, "Nonce too low")
|
||||
require(n =< state.nonce, "Nonce too high")
|
||||
put(state{ nonce = n + 1 })
|
||||
|
||||
@@ -5,5 +5,5 @@ contract Counter =
|
||||
|
||||
function init(val) = { value = val }
|
||||
function get() = state.value
|
||||
function tick() = put(state{ value = state.value + 1 })
|
||||
stateful function tick() = put(state{ value = state.value + 1 })
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ contract DutchAuction =
|
||||
sold : bool }
|
||||
|
||||
// Add to work around current lack of predefined functions
|
||||
private function spend(to, amount) =
|
||||
private stateful function spend(to, amount) =
|
||||
let total = Contract.balance
|
||||
Chain.spend(to, amount)
|
||||
total - amount
|
||||
|
||||
@@ -12,7 +12,7 @@ contract Environment =
|
||||
|
||||
function init(remote) = {remote = remote}
|
||||
|
||||
function set_remote(remote) = put({remote = remote})
|
||||
stateful function set_remote(remote) = put({remote = remote})
|
||||
|
||||
// -- Information about the this contract ---
|
||||
|
||||
@@ -38,7 +38,7 @@ contract Environment =
|
||||
|
||||
// Value
|
||||
function call_value() : int = Call.value
|
||||
function nested_value(value : int) : int =
|
||||
stateful function nested_value(value : int) : int =
|
||||
state.remote.call_value(value = value / 2)
|
||||
|
||||
// Gas price
|
||||
|
||||
@@ -9,7 +9,7 @@ contract Factorial =
|
||||
|
||||
function init(worker) = {worker = worker}
|
||||
|
||||
function set_worker(worker) = put(state{worker = worker})
|
||||
stateful function set_worker(worker) = put(state{worker = worker})
|
||||
|
||||
function fac(x : int) : int =
|
||||
if(x == 0) 1
|
||||
|
||||
@@ -15,7 +15,7 @@ contract FundMe =
|
||||
private function require(b : bool, err : string) =
|
||||
if(!b) abort(err)
|
||||
|
||||
private function spend(args : spend_args) =
|
||||
private stateful function spend(args : spend_args) =
|
||||
Chain.spend(args.recipient, args.amount)
|
||||
|
||||
public function init(beneficiary, deadline, goal) : state =
|
||||
|
||||
+12
-12
@@ -17,8 +17,8 @@ contract Maps =
|
||||
{ ["one"] = {x = 1, y = 2},
|
||||
["two"] = {x = 3, y = 4},
|
||||
["three"] = {x = 5, y = 6} }
|
||||
function map_state_i() = put(state{ map_i = map_i() })
|
||||
function map_state_s() = put(state{ map_s = map_s() })
|
||||
stateful function map_state_i() = put(state{ map_i = map_i() })
|
||||
stateful function map_state_s() = put(state{ map_s = map_s() })
|
||||
|
||||
// m[k]
|
||||
function get_i(k, m : map(int, pt)) = m[k]
|
||||
@@ -35,20 +35,20 @@ contract Maps =
|
||||
// m{[k] = v}
|
||||
function set_i(k, p, m : map(int, pt)) = m{ [k] = p }
|
||||
function set_s(k, p, m : map(string, pt)) = m{ [k] = p }
|
||||
function set_state_i(k, p) = put(state{ map_i = set_i(k, p, state.map_i) })
|
||||
function set_state_s(k, p) = put(state{ map_s = set_s(k, p, state.map_s) })
|
||||
stateful function set_state_i(k, p) = put(state{ map_i = set_i(k, p, state.map_i) })
|
||||
stateful function set_state_s(k, p) = put(state{ map_s = set_s(k, p, state.map_s) })
|
||||
|
||||
// m{f[k].x = v}
|
||||
function setx_i(k, x, m : map(int, pt)) = m{ [k].x = x }
|
||||
function setx_s(k, x, m : map(string, pt)) = m{ [k].x = x }
|
||||
function setx_state_i(k, x) = put(state{ map_i[k].x = x })
|
||||
function setx_state_s(k, x) = put(state{ map_s[k].x = x })
|
||||
stateful function setx_state_i(k, x) = put(state{ map_i[k].x = x })
|
||||
stateful function setx_state_s(k, x) = put(state{ map_s[k].x = x })
|
||||
|
||||
// m{[k] @ x = v }
|
||||
function addx_i(k, d, m : map(int, pt)) = m{ [k].x @ x = x + d }
|
||||
function addx_s(k, d, m : map(string, pt)) = m{ [k].x @ x = x + d }
|
||||
function addx_state_i(k, d) = put(state{ map_i[k].x @ x = x + d })
|
||||
function addx_state_s(k, d) = put(state{ map_s[k].x @ x = x + d })
|
||||
stateful function addx_state_i(k, d) = put(state{ map_i[k].x @ x = x + d })
|
||||
stateful function addx_state_s(k, d) = put(state{ map_s[k].x @ x = x + d })
|
||||
|
||||
// m{[k = def] @ x = v }
|
||||
function addx_def_i(k, v, d, m : map(int, pt)) = m{ [k = v].x @ x = x + d }
|
||||
@@ -77,8 +77,8 @@ contract Maps =
|
||||
// Map.delete
|
||||
function delete_i(k, m : map(int, pt)) = Map.delete(k, m)
|
||||
function delete_s(k, m : map(string, pt)) = Map.delete(k, m)
|
||||
function delete_state_i(k) = put(state{ map_i = delete_i(k, state.map_i) })
|
||||
function delete_state_s(k) = put(state{ map_s = delete_s(k, state.map_s) })
|
||||
stateful function delete_state_i(k) = put(state{ map_i = delete_i(k, state.map_i) })
|
||||
stateful function delete_state_s(k) = put(state{ map_s = delete_s(k, state.map_s) })
|
||||
|
||||
// Map.size
|
||||
function size_i(m : map(int, pt)) = Map.size(m)
|
||||
@@ -95,6 +95,6 @@ contract Maps =
|
||||
// Map.from_list
|
||||
function fromlist_i(xs : list((int, pt))) = Map.from_list(xs)
|
||||
function fromlist_s(xs : list((string, pt))) = Map.from_list(xs)
|
||||
function fromlist_state_i(xs) = put(state{ map_i = fromlist_i(xs) })
|
||||
function fromlist_state_s(xs) = put(state{ map_s = fromlist_s(xs) })
|
||||
stateful function fromlist_state_i(xs) = put(state{ map_i = fromlist_i(xs) })
|
||||
stateful function fromlist_state_s(xs) = put(state{ map_s = fromlist_s(xs) })
|
||||
|
||||
|
||||
+13
-13
@@ -9,22 +9,22 @@ contract Oracles =
|
||||
type oracle_id = oracle(query_t, answer_t)
|
||||
type query_id = oracle_query(query_t, answer_t)
|
||||
|
||||
function registerOracle(acct : address,
|
||||
stateful function registerOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle_id =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
function registerIntIntOracle(acct : address,
|
||||
stateful function registerIntIntOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle(int, int) =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
function registerStringStringOracle(acct : address,
|
||||
stateful function registerStringStringOracle(acct : address,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle(string, string) =
|
||||
Oracle.register(acct, qfee, ttl)
|
||||
|
||||
function signedRegisterOracle(acct : address,
|
||||
stateful function signedRegisterOracle(acct : address,
|
||||
sign : signature,
|
||||
qfee : fee,
|
||||
ttl : ttl) : oracle_id =
|
||||
@@ -33,7 +33,7 @@ contract Oracles =
|
||||
function queryFee(o : oracle_id) : fee =
|
||||
Oracle.query_fee(o)
|
||||
|
||||
function createQuery(o : oracle_id,
|
||||
stateful function createQuery(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
@@ -42,7 +42,7 @@ contract Oracles =
|
||||
Oracle.query(o, q, qfee, qttl, rttl)
|
||||
|
||||
// Do not use in production!
|
||||
function unsafeCreateQuery(o : oracle_id,
|
||||
stateful function unsafeCreateQuery(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
@@ -50,7 +50,7 @@ contract Oracles =
|
||||
Oracle.query(o, q, qfee, qttl, rttl)
|
||||
|
||||
// Do not use in production!
|
||||
function unsafeCreateQueryThenErr(o : oracle_id,
|
||||
stateful function unsafeCreateQueryThenErr(o : oracle_id,
|
||||
q : query_t,
|
||||
qfee : fee,
|
||||
qttl : ttl,
|
||||
@@ -59,21 +59,21 @@ contract Oracles =
|
||||
require(qfee >= 100000000000000000, "causing a late error")
|
||||
res
|
||||
|
||||
function extendOracle(o : oracle_id,
|
||||
stateful function extendOracle(o : oracle_id,
|
||||
ttl : ttl) : () =
|
||||
Oracle.extend(o, ttl)
|
||||
|
||||
function signedExtendOracle(o : oracle_id,
|
||||
stateful function signedExtendOracle(o : oracle_id,
|
||||
sign : signature, // Signed oracle address
|
||||
ttl : ttl) : () =
|
||||
Oracle.extend(o, signature = sign, ttl)
|
||||
|
||||
function respond(o : oracle_id,
|
||||
stateful function respond(o : oracle_id,
|
||||
q : query_id,
|
||||
r : answer_t) : () =
|
||||
Oracle.respond(o, q, r)
|
||||
|
||||
function signedRespond(o : oracle_id,
|
||||
stateful function signedRespond(o : oracle_id,
|
||||
q : query_id,
|
||||
sign : signature,
|
||||
r : answer_t) : () =
|
||||
@@ -96,13 +96,13 @@ contract Oracles =
|
||||
datatype complexQuestion = Why(int) | How(string)
|
||||
datatype complexAnswer = NoAnswer | Answer(complexQuestion, string, int)
|
||||
|
||||
function complexOracle(question) =
|
||||
stateful function complexOracle(question) =
|
||||
let o = Oracle.register(Contract.address, 0, FixedTTL(1000)) : oracle(complexQuestion, complexAnswer)
|
||||
let q = Oracle.query(o, question, 0, RelativeTTL(100), RelativeTTL(100))
|
||||
Oracle.respond(o, q, Answer(question, "magic", 1337))
|
||||
Oracle.get_answer(o, q)
|
||||
|
||||
function signedComplexOracle(question, sig) =
|
||||
stateful function signedComplexOracle(question, sig) =
|
||||
let o = Oracle.register(signature = sig, Contract.address, 0, FixedTTL(1000)) : oracle(complexQuestion, complexAnswer)
|
||||
let q = Oracle.query(o, question, 0, RelativeTTL(100), RelativeTTL(100))
|
||||
Oracle.respond(o, q, Answer(question, "magic", 1337), signature = sig)
|
||||
|
||||
@@ -11,7 +11,7 @@ contract Remote3 =
|
||||
|
||||
contract RemoteCall =
|
||||
|
||||
function call(r : Remote1, x : int) : int =
|
||||
stateful function call(r : Remote1, x : int) : int =
|
||||
r.main(gas = 10000, value = 10, x)
|
||||
|
||||
function staged_call(r1 : Remote1, r2 : Remote2, x : int) =
|
||||
|
||||
@@ -24,5 +24,5 @@ contract SimpleStorage =
|
||||
|
||||
function get() : int = state.data
|
||||
|
||||
function set(value : int) =
|
||||
stateful function set(value : int) =
|
||||
put(state{data = value})
|
||||
|
||||
@@ -4,19 +4,19 @@ contract SpendContract =
|
||||
|
||||
contract SpendTest =
|
||||
|
||||
function spend(to, amount) =
|
||||
stateful function spend(to, amount) =
|
||||
let total = Contract.balance
|
||||
Chain.spend(to, amount)
|
||||
total - amount
|
||||
|
||||
function withdraw(amount) : int =
|
||||
stateful function withdraw(amount) : int =
|
||||
spend(Call.caller, amount)
|
||||
|
||||
function withdraw_from(account, amount) =
|
||||
stateful function withdraw_from(account, amount) =
|
||||
account.withdraw(amount)
|
||||
withdraw(amount)
|
||||
|
||||
function spend_from(from, to, amount) =
|
||||
stateful function spend_from(from, to, amount) =
|
||||
from.withdraw(amount)
|
||||
Chain.spend(to, amount)
|
||||
Chain.balance(to)
|
||||
|
||||
@@ -27,13 +27,13 @@ contract StateHandling =
|
||||
function read_s() = state.s
|
||||
function read_m() = state.m
|
||||
|
||||
function update(new_state : state) = put(new_state)
|
||||
function update_i(new_i) = put(state{ i = new_i })
|
||||
function update_s(new_s) = put(state{ s = new_s })
|
||||
function update_m(new_m) = put(state{ m = new_m })
|
||||
stateful function update(new_state : state) = put(new_state)
|
||||
stateful function update_i(new_i) = put(state{ i = new_i })
|
||||
stateful function update_s(new_s) = put(state{ s = new_s })
|
||||
stateful function update_m(new_m) = put(state{ m = new_m })
|
||||
|
||||
function pass_it(r : Remote) = r.look_at(state)
|
||||
function nop(r : Remote) = put(state{ i = state.i })
|
||||
stateful function nop(r : Remote) = put(state{ i = state.i })
|
||||
function return_it_s(r : Remote, big : bool) =
|
||||
let x = r.return_s(big)
|
||||
String.length(x)
|
||||
@@ -50,10 +50,10 @@ contract StateHandling =
|
||||
function pass_update_s(r : Remote, s) = r.fun_update_s(state, s)
|
||||
function pass_update_m(r : Remote, m) = r.fun_update_m(state, m)
|
||||
|
||||
function remote_update_i (r : Remote, i) = put(r.fun_update_i(state, i))
|
||||
function remote_update_s (r : Remote, s) = put(r.fun_update_s(state, s))
|
||||
function remote_update_m (r : Remote, m) = put(r.fun_update_m(state, m))
|
||||
function remote_update_mk(r : Remote, k, v) = put(r.fun_update_mk(state, k, v))
|
||||
stateful function remote_update_i (r : Remote, i) = put(r.fun_update_i(state, i))
|
||||
stateful function remote_update_s (r : Remote, s) = put(r.fun_update_s(state, s))
|
||||
stateful function remote_update_m (r : Remote, m) = put(r.fun_update_m(state, m))
|
||||
stateful function remote_update_mk(r : Remote, k, v) = put(r.fun_update_mk(state, k, v))
|
||||
|
||||
// remote called
|
||||
function look_at(s : state) = ()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
contract Remote =
|
||||
stateful function remote_spend : (address, int) => ()
|
||||
function remote_pure : int => int
|
||||
|
||||
contract Stateful =
|
||||
|
||||
private function pure(x) = x + 1
|
||||
private stateful function local_spend(a) =
|
||||
Chain.spend(a, 1000)
|
||||
|
||||
// Non-stateful functions cannot mention stateful functions
|
||||
function fail1(a : address) = Chain.spend(a, 1000)
|
||||
function fail2(a : address) = local_spend(a)
|
||||
function fail3(a : address) =
|
||||
let foo = Chain.spend
|
||||
foo(a, 1000)
|
||||
|
||||
// Private functions must also be annotated
|
||||
private function fail4(a) = Chain.spend(a, 1000)
|
||||
|
||||
// If annotated, stateful functions are allowed
|
||||
stateful function ok1(a : address) = Chain.spend(a, 1000)
|
||||
|
||||
// And pure functions are always allowed
|
||||
stateful function ok2(a : address) = pure(5)
|
||||
stateful function ok3(a : address) =
|
||||
let foo = pure
|
||||
foo(5)
|
||||
|
||||
// No error here (fail4 is annotated as not stateful)
|
||||
function ok4(a : address) = fail4(a)
|
||||
|
||||
// Lamdbas are checked at the construction site
|
||||
private function fail5() : address => () = (a) => Chain.spend(a, 1000)
|
||||
|
||||
// .. so you can pass a stateful lambda to a non-stateful higher-order
|
||||
// function:
|
||||
private function apply(f : 'a => 'b, x) = f(x)
|
||||
stateful function ok5(a : address) =
|
||||
apply((val) => Chain.spend(a, val), 1000)
|
||||
|
||||
// It doesn't matter if remote calls are stateful or not
|
||||
function ok6(r : Remote) = r.remote_spend(Contract.address, 1000)
|
||||
function ok7(r : Remote) = r.remote_pure(5)
|
||||
|
||||
// But you can't send any tokens if not stateful
|
||||
function fail6(r : Remote) = r.remote_spend(value = 1000, Contract.address, 1000)
|
||||
function fail7(r : Remote) = r.remote_pure(value = 1000, 5)
|
||||
function fail8(r : Remote) =
|
||||
let foo = r.remote_pure
|
||||
foo(value = 1000, 5)
|
||||
function ok8(r : Remote) = r.remote_spend(Contract.address, 1000, value = 0)
|
||||
|
||||
@@ -11,11 +11,11 @@ contract VariantTypes =
|
||||
|
||||
function require(b) = if(!b) abort("required")
|
||||
|
||||
function start(bal : int) =
|
||||
stateful function start(bal : int) =
|
||||
switch(state)
|
||||
Stopped => put(Started({owner = Call.caller, balance = bal, color = Grey(0)}))
|
||||
|
||||
function stop() =
|
||||
stateful function stop() =
|
||||
switch(state)
|
||||
Started(st) =>
|
||||
require(Call.caller == st.owner)
|
||||
@@ -23,7 +23,7 @@ contract VariantTypes =
|
||||
st.balance
|
||||
|
||||
function get_color() = switch(state) Started(st) => st.color
|
||||
function set_color(c) = switch(state) Started(st) => put(Started(st{color = c}))
|
||||
stateful function set_color(c) = switch(state) Started(st) => put(Started(st{color = c}))
|
||||
|
||||
function get_state() = state
|
||||
|
||||
|
||||
Reference in New Issue
Block a user