First test work commit, don't touch

This commit is contained in:
Robert Virding
2018-12-22 01:23:40 +01:00
parent 3ceb8c38db
commit d4d02fd576
97 changed files with 10599 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
-module(aeso_abi_tests).
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).
-define(SANDBOX(Code), sandbox(fun() -> Code end)).
sandbox(Code) ->
Parent = self(),
Tag = make_ref(),
{Pid, Ref} = spawn_monitor(fun() -> Parent ! {Tag, Code()} end),
receive
{Tag, Res} -> erlang:demonitor(Ref, [flush]), {ok, Res};
{'DOWN', Ref, process, Pid, Reason} -> {error, Reason}
after 100 ->
exit(Pid, kill),
{error, loop}
end.
malicious_from_binary_test() ->
CircularList = from_words([32, 1, 32]), %% Xs = 1 :: Xs
{ok, {error, circular_references}} = ?SANDBOX(aeso_heap:from_binary({list, word}, CircularList)),
{ok, {error, {binary_too_short, _}}} = ?SANDBOX(aeso_heap:from_binary(word, <<1, 2, 3, 4>>)),
ok.
from_words(Ws) ->
<< <<(from_word(W))/binary>> || W <- Ws >>.
from_word(W) when is_integer(W) ->
<<W:256>>;
from_word(S) when is_list(S) ->
Len = length(S),
Bin = <<(list_to_binary(S))/binary, 0:(32 - Len)/unit:8>>,
<<Len:256, Bin/binary>>.
encode_decode_test() ->
encode_decode(word, 42),
42 = encode_decode(word, 42),
-1 = encode_decode(signed_word, -1),
<<"Hello world">> = encode_decode(string, <<"Hello world">>),
{} = encode_decode({tuple, []}, {}),
{42} = encode_decode({tuple, [word]}, {42}),
{42, 0} = encode_decode({tuple, [word, word]}, {42, 0}),
[] = encode_decode({list, word}, []),
[32] = encode_decode({list, word}, [32]),
none = encode_decode({option, word}, none),
{some, 1} = encode_decode({option, word}, {some, 1}),
string = encode_decode(typerep, string),
word = encode_decode(typerep, word),
{list, word} = encode_decode(typerep, {list, word}),
{tuple, [word]} = encode_decode(typerep, {tuple, [word]}),
1 = encode_decode(word, 1),
0 = encode_decode(word, 0),
ok.
encode_decode_sophia_test() ->
{42} = encode_decode_sophia_string("int", "42"),
{1} = encode_decode_sophia_string("bool", "true"),
{0} = encode_decode_sophia_string("bool", "false"),
{<<"Hello">>} = encode_decode_sophia_string("string", "\"Hello\""),
{<<"Hello">>, [1,2,3], {variant, 1, [1]}} =
encode_decode_sophia_string(
"(string, list(int), option(bool))",
"\"Hello\", [1,2,3], Some(true)"),
ok.
encode_decode_sophia_string(SophiaType, String) ->
io:format("String ~p~n", [String]),
Code = [ "contract Call =\n"
, " function foo : ", SophiaType, " => _\n"
, " function __call() = foo(", String, ")\n" ],
{ok, _, {Types, _}, Args} = aeso_compiler:check_call(lists:flatten(Code), []),
Arg = list_to_tuple(Args),
Type = {tuple, Types},
io:format("Type ~p~n", [Type]),
Data = encode(Arg),
decode(Type, Data).
encode_decode(T, D) ->
?assertEqual(D, decode(T, encode(D))),
D.
encode(D) ->
aeso_heap:to_binary(D).
decode(T,B) ->
{ok, D} = aeso_heap:from_binary(T, B),
D.
+136
View File
@@ -0,0 +1,136 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc Test Sophia language compiler.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_compiler_tests).
-include_lib("eunit/include/eunit.hrl").
%% simple_compile_test_() -> ok.
%% Very simply test compile the given contracts. Only basic checks
%% are made on the output, just that it is a binary which indicates
%% that the compilation worked.
simple_compile_test_() ->
{setup,
fun () -> ok end, %Setup
fun (_) -> ok end, %Cleanup
[ {"Testing the " ++ ContractName ++ " contract",
fun() ->
#{byte_code := ByteCode,
contract_source := _,
type_info := _} = compile(ContractName),
?assertMatch(Code when is_binary(Code), ByteCode)
end} || ContractName <- compilable_contracts() ] ++
[ {"Testing error messages of " ++ ContractName,
fun() ->
{type_errors, Errors} = compile(ContractName),
?assertEqual(lists:sort(ExpectedErrors), lists:sort(Errors))
end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ]
}.
compile(Name) ->
try
aeso_compiler:from_string(aeso_test_utils:read_contract(Name), [])
catch _:{type_errors, _} = E ->
E
end.
%% compilable_contracts() -> [ContractName].
%% The currently compilable contracts.
compilable_contracts() ->
["complex_types",
"counter",
"dutch_auction",
"environment",
"factorial",
"fundme",
"identity",
"maps",
"oracles",
"remote_call",
"simple",
"simple_storage",
"spend_test",
"stack",
"test",
"builtin_bug",
"builtin_map_get_bug"
].
%% Contracts that should produce type errors
failing_contracts() ->
[ {"name_clash",
["Duplicate definitions of abort at\n - (builtin location)\n - line 14, column 3\n",
"Duplicate definitions of double_def at\n - line 10, column 3\n - line 11, column 3\n",
"Duplicate definitions of double_proto at\n - line 4, column 3\n - line 5, column 3\n",
"Duplicate definitions of proto_and_def at\n - line 7, column 3\n - line 8, column 3\n",
"Duplicate definitions of put at\n - (builtin location)\n - line 15, column 3\n",
"Duplicate definitions of state at\n - (builtin location)\n - line 16, column 3\n"]}
, {"type_errors",
["Unbound variable zz at line 17, column 21\n",
"Cannot unify int\n"
" and list(int)\n"
"when checking the application at line 26, column 9 of\n"
" (::) : (int, list(int)) => list(int)\n"
"to arguments\n"
" x : int\n"
" x : int\n",
"Cannot unify string\n"
" and int\n"
"when checking the assignment of the field\n"
" x : map(string, string) (at line 9, column 46)\n"
"to the old value __x and the new value\n"
" __x {[\"foo\"] @ x = x + 1} : map(string, int)\n",
"Cannot unify int\n"
" and string\n"
"when checking the type of the expression at line 34, column 45\n"
" 1 : int\n"
"against the expected type\n"
" string\n",
"Cannot unify string\n"
" and int\n"
"when checking the type of the expression at line 34, column 50\n"
" \"bla\" : string\n"
"against the expected type\n"
" int\n",
"Cannot unify string\n"
" and int\n"
"when checking the type of the expression at line 32, column 18\n"
" \"x\" : string\n"
"against the expected type\n"
" int\n",
"Cannot unify string\n"
" and int\n"
"when checking the type of the expression at line 11, column 56\n"
" \"foo\" : string\n"
"against the expected type\n"
" int\n",
"Cannot unify int\n"
" and string\n"
"when comparing the types of the if-branches\n"
" - w : int (at line 38, column 13)\n"
" - z : string (at line 39, column 10)\n",
"Not a record type: string\n"
"arising from the projection of the field y (at line 22, column 38)\n",
"Not a record type: string\n"
"arising from an assignment of the field y (at line 21, column 42)\n",
"Not a record type: string\n"
"arising from an assignment of the field y (at line 20, column 38)\n",
"Not a record type: string\n"
"arising from an assignment of the field y (at line 19, column 35)\n",
"Ambiguous record type with field y (at line 13, column 25) could be one of\n"
" - r (at line 4, column 10)\n"
" - r' (at line 5, column 10)\n",
"Record type r2 does not have field y (at line 15, column 22)\n",
"Repeated name x in pattern\n"
" x :: x (at line 26, column 7)\n",
"No record type with fields y, z (at line 14, column 22)\n"]}
].
+20
View File
@@ -0,0 +1,20 @@
-module(aeso_eunit_SUITE).
-compile([export_all, nowarn_export_all]).
-include_lib("common_test/include/ct.hrl").
all() ->
[{group, eunit}].
groups() ->
[{eunit, [], [ aeso_scan_tests
, aeso_parser_tests
, aeso_compiler_tests
, aeso_abi_tests
]}].
aeso_scan_tests(_Config) -> ok = eunit:test(aeso_scan_tests).
aeso_parser_tests(_Config) -> ok = eunit:test(aeso_parser_tests).
aeso_compiler_tests(_Config) -> ok = eunit:test(aeso_compiler_tests).
aeso_abi_tests(_Config) -> ok = eunit:test(aeso_abi_tests).
+111
View File
@@ -0,0 +1,111 @@
-module(aeso_parser_tests).
-export([parse_contract/1]).
-include_lib("eunit/include/eunit.hrl").
simple_contracts_test_() ->
{foreach,
fun() -> ok end,
fun(_) -> ok end,
[{"Parse a contract with an identity function.",
fun() ->
Text = "contract Identity =\n"
" function id(x) = x\n",
?assertMatch(
[{contract, _, {con, _, "Identity"},
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"},
{id, _, "x"}}]}], parse_string(Text)),
ok
end},
{"Operator precedence test.",
fun() ->
NoPar = fun NoPar(X) when is_atom(X) -> atom_to_list(X);
NoPar({A, Op, B}) -> lists:concat([NoPar(A), " ", Op, " ", NoPar(B)]);
NoPar({Op, A}) -> lists:concat([Op, " ", NoPar(A)])
end,
Par = fun Par(X) when is_atom(X) -> atom_to_list(X);
Par({A, Op, B}) -> lists:concat(["(", Par(A), " ", Op, " ", Par(B), ")"]);
Par({Op, A}) -> lists:concat(["(", Op, " ", Par(A), ")"])
end,
Parse = fun(S) ->
try remove_line_numbers(parse_expr(S))
catch _:_ -> ?assertMatch(ok, {parse_fail, S}) end
end,
CheckParens = fun(Expr) ->
?assertEqual(Parse(NoPar(Expr)), Parse(Par(Expr)))
end,
LeftAssoc = fun(Op) -> CheckParens({{a, Op, b}, Op, c}) end,
RightAssoc = fun(Op) -> CheckParens({a, Op, {b, Op, c}}) end,
NonAssoc = fun(Op) ->
OpAtom = list_to_atom(Op),
?assertError({error, {_, parse_error, _}},
parse_expr(NoPar({a, Op, {b, Op, c}}))) end,
Stronger = fun(Op1, Op2) ->
CheckParens({{a, Op1, b}, Op2, c}),
CheckParens({a, Op2, {b, Op1, c}})
end,
Tiers = [["||"], ["&&"], ["==", "!=", "<", ">", "=<", ">="], ["::", "++"],
["+", "-"], ["*", "/", "mod"]],
%% associativity
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
%% precedence
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
ok
end}
] ++
%% Parse tests of example contracts
[ {lists:concat(["Parse the ", Contract, " contract."]),
fun() -> roundtrip_contract(Contract) end}
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, withdrawal, fundme, dutch_auction] ]
}.
parse_contract(Name) ->
parse_string(aeso_test_utils:read_contract(Name)).
roundtrip_contract(Name) ->
round_trip(aeso_test_utils:read_contract(Name)).
parse_string(Text) ->
case aeso_parser:string(Text) of
{ok, Contract} -> Contract;
Err -> error(Err)
end.
parse_expr(Text) ->
[{letval, _, _, _, Expr}] =
parse_string("let _ = " ++ Text),
Expr.
round_trip(Text) ->
Contract = parse_string(Text),
Text1 = prettypr:format(aeso_pretty:decls(Contract)),
Contract1 = parse_string(Text1),
NoSrcLoc = remove_line_numbers(Contract),
NoSrcLoc1 = remove_line_numbers(Contract1),
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
remove_line_numbers({line, _L}) -> {line, 0};
remove_line_numbers({col, _C}) -> {col, 0};
remove_line_numbers([H|T]) ->
[remove_line_numbers(H) | remove_line_numbers(T)];
remove_line_numbers(T) when is_tuple(T) ->
list_to_tuple(remove_line_numbers(tuple_to_list(T)));
remove_line_numbers(M) when is_map(M) ->
maps:from_list(remove_line_numbers(maps:to_list(M)));
remove_line_numbers(X) -> X.
diff(X, X) -> X;
diff([H | T], [H1 | T1]) ->
[diff(H, H1) | diff(T, T1)];
diff(T, T1) when tuple_size(T) == tuple_size(T1) ->
list_to_tuple(diff(tuple_to_list(T), tuple_to_list(T1)));
diff(X, Y) -> {X, '/=', Y}.
tails(Zs) -> lists:foldr(fun(X, [Xs|Xss]) -> [[X|Xs], Xs | Xss] end, [[]], Zs).
+84
View File
@@ -0,0 +1,84 @@
-module(aeso_scan_tests).
-include_lib("eunit/include/eunit.hrl").
empty_contract_test_() ->
{foreach,
fun() ->
ok
end,
fun(_) ->
ok
end,
[{"Scan an empty contract.",
fun() ->
Text = " ",
{ok, []} = aeso_scan:scan(Text),
ok
end}
]}.
all_tokens_test_() ->
{foreach, fun() -> ok end,
fun(_) -> ok end,
[{"Check that we can scan all tokens.",
fun() ->
Tokens = all_tokens(),
Text = string:join(lists:map(fun show_token/1, Tokens), " "),
io:format("~s\n", [Text]),
{ok, Tokens1} = aeso_scan:scan(Text),
true = compare_tokens(Tokens, Tokens1),
ok
end}
]}.
all_tokens() ->
Lit = fun(T) -> {T, 1} end,
Tok = fun(T, V) -> {T, 1, V} end,
Hash = list_to_binary([ I * 8 || I <- lists:seq(0, 31) ]),
%% Symbols
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
%% Operators
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
%% Keywords
lists:map(Lit, [contract, type, 'let', switch, rec, 'and']) ++
%% Comment token (not an actual token), just for tests
[{comment, 0, "// *Comment!\"\n"},
{comment, 0, "/* bla /* bla bla */*/"}] ++
%% Literals
[ Lit(true), Lit(false)
, Tok(id, "foo"), Tok(id, "_"), Tok(con, "Foo")
, Tok(hash, Hash)
, Tok(int, 1234567890), Tok(hex, 9876543210)
, Tok(string, <<"bla\"\\\b\e\f\n\r\t\vbla">>)
].
compare_tokens([], []) -> true;
compare_tokens([{T, _} | Ts1], [{T, _} | Ts2]) ->
compare_tokens(Ts1, Ts2);
compare_tokens([{T, _, V} | Ts1], [{T, _, V} | Ts2]) ->
compare_tokens(Ts1, Ts2);
compare_tokens([{comment, _, _} | Ts1], Ts2) ->
compare_tokens(Ts1, Ts2);
compare_tokens(Ts1, Ts2) ->
case length(Ts1) == length(Ts2) of
true ->
{token_mismatch, [ {expected, T1, got, T2} || {T1, T2} <- lists:zip(Ts1, Ts2), T1 /= T2]};
false ->
{token_mismatch, {expected, Ts1, got, Ts2}}
end.
fmt(X) -> fmt("~p", X).
fmt(Fmt, X) -> lists:flatten(io_lib:format(Fmt, [X])).
show_token({T, _}) -> atom_to_list(T);
show_token({id, _, X}) -> X;
show_token({con, _, C}) -> C;
show_token({param, _, P}) -> "@" ++ P;
show_token({string, _, S}) -> fmt(binary_to_list(S));
show_token({int, _, N}) -> fmt(N);
show_token({hex, _, N}) -> fmt("0x~.16b", N);
show_token({hash, _, <<N:256>>}) -> fmt("#~.16b", N);
show_token({comment, _, S}) -> S;
show_token({_, _, _}) -> "TODO".
+160
View File
@@ -0,0 +1,160 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc Test utilities for the Sophia language tests.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeso_test_utils).
-include("apps/aecontract/src/aecontract.hrl").
-export([read_contract/1, contract_path/0, pp/1, pp/2,
dump_words/1, show_heap/1, show_heap/2, show_heap_value/1, compile/1]).
-export([spend/3, get_balance/2, call_contract/6, get_store/1, set_store/2,
aens_lookup/4]).
contract_path() ->
{ok, Cwd} = file:get_cwd(),
N = length(filename:split(Cwd)),
Rel = ["apps", "aesophia", "test", "contracts"],
%% Try the first matching directory (../)*Rel
Cand = fun(I) -> filename:join(lists:duplicate(I, "..") ++ Rel) end,
case [ Dir || Dir <- lists:map(Cand, lists:seq(0, N)), filelib:is_dir(Dir) ] of
[Dir | _] -> Dir;
[] -> error(failed_to_find_contract_dir)
end.
%% Read a contract file from the test/contracts directory.
-spec read_contract(string() | atom()) -> string().
read_contract(Name) ->
{ok, Bin} = file:read_file(filename:join(contract_path(), lists:concat([Name, ".aes"]))),
binary_to_list(Bin).
pp(Name) -> pp(Name, []).
pp(Name, Options) ->
case aeso_parser:string(read_contract(Name)) of
{ok, AST} ->
[ io:format("~s\n", [prettypr:format(aeso_pretty:decls(AST))]) || not lists:member(quiet, Options) ];
{error, {{L, C}, parse_error, Err}} ->
io:format("Parse error at ~p:~p:~p\n~s\n", [Name, L, C, Err])
end.
compile(Name) ->
aeso_compiler:from_string(read_contract(Name),
[pp_sophia_code, pp_typed_ast, pp_icode]).
%% Stack simulator
simulate([],Stack) ->
Stack;
simulate(['PUSH1',X|More],S) ->
simulate(More,[X|S]);
simulate([Op|More],Stack) ->
simulate(More,simulate(Op,Stack));
simulate('MSIZE',S) ->
A = new_atom(),
io:format("~p = MSIZE\n",[A]),
[A|S];
simulate('DUP2',[A,B|S]) ->
[B,A,B|S];
simulate('DUP3',[A,B,C|S]) ->
[C,A,B,C|S];
simulate('ADD',[A,B|S]) ->
[add(A,B)|S];
simulate('MSTORE',[Addr,X|S]) ->
io:format("mem(~p) <- ~p\n",[Addr,X]),
S;
simulate('MLOAD',[Addr|S]) ->
A = new_atom(),
io:format("~p = mem(~p)\n",[A,Addr]),
[A|S];
simulate('SWAP1',[A,B|S]) ->
[B,A|S];
simulate('SWAP2',[A,B,C|S]) ->
[C,B,A|S];
simulate('SUB',[A,B|S]) ->
[{A,'-',B}|S];
simulate('POP',[_|S]) ->
S.
add(0,X) ->
X;
add(X,0) ->
X;
add(X,{A,'-',X}) ->
A;
add(X,{A,'+',B}) ->
{A,'+',add(X,B)};
add(A,B) ->
{A,'+',B}.
new_atom() ->
catch ets:new(names,[set,public,named_table]),
case ets:lookup(names,index) of
[] -> I = 0;
[{index,I}] -> ok
end,
ets:insert(names,{index,I+1}),
list_to_atom([$a+I]).
show_heap(Bin) ->
show_heap(0, Bin).
show_heap(Offs, Bin) ->
Words = dump_words(Bin),
Addrs = lists:seq(0, (length(Words) - 1) * 32, 32),
lists:flatten([io_lib:format("~4b ~p\n", [Addr + Offs, Word]) || {Addr, Word} <- lists:zip(Addrs, Words)]).
show_heap_value(HeapValue) ->
Maps = aeso_heap:heap_value_maps(HeapValue),
Offs = aeso_heap:heap_value_offset(HeapValue),
Ptr = aeso_heap:heap_value_pointer(HeapValue),
Mem = aeso_heap:heap_value_heap(HeapValue),
Words = dump_words(Mem),
Addrs = lists:seq(Offs, Offs + (length(Words) - 1) * 32, 32),
lists:flatten(
io_lib:format(" Maps: ~p\n Ptr: ~p\n Heap: ~p",
[Maps, Ptr, lists:zip(Addrs, Words)])).
%% Translate a blob of 256-bit words into readable form. Does a bit of guessing
%% to recover strings. TODO: strings longer than 32 bytes
dump_words(Bin) -> dump_words(Bin, []).
dump_words(<<N:256, W:32/binary, Rest/binary>>, Acc) when N < 32 ->
NotN = (32 - N) * 8,
case W of
<<S:N/binary, 0:NotN>> ->
Str = binary_to_list(S),
case lists:member(0, Str) of
true -> dump_words(<<W/binary, Rest/binary>>, [N | Acc]); %% Not a string
false -> dump_words(Rest, [binary_to_list(S), N | Acc])
end;
_ -> dump_words(<<W/binary, Rest/binary>>, [N | Acc])
end;
dump_words(<<N:256/signed, Rest/binary>>, Acc) ->
dump_words(Rest, [N | Acc]);
dump_words(<<>>, Acc) -> lists:reverse(Acc);
dump_words(Rest, Acc) -> lists:reverse([{error, Rest} | Acc]).
%% -- Chain API for test -----------------------------------------------------
aens_lookup(Name, Key, Type, _S) ->
io:format("aens_lookup(~p, ~p, ~p)\n", [Name, Key, Type]),
{ok, {some, <<0:32/unit:8>>}}.
spend(Recipient, Amount, S) ->
io:format("+++ SPEND(~p, ~p)\n", [Recipient, Amount]),
{ok, S}.
get_balance(_, _) -> 1000000.
call_contract(Contract, Gas, Value, CallData, CallStack, S) ->
io:format("+++ CALL(~p, ~p, ~p, ~p, ~p)\n", [Contract, Gas, Value, CallData, CallStack]),
{ok, <<42:256>>, S}.
get_store(_) -> #{}.
set_store(_, _) -> ok.
+28
View File
@@ -0,0 +1,28 @@
-module(contract_tests).
-include_lib("eunit/include/eunit.hrl").
make_cmd() -> "make -C " ++ aeso_test_utils:contract_path().
contracts_test_() ->
{setup,
fun() -> os:cmd(make_cmd()) end,
fun(_) -> os:cmd(make_cmd() ++ " clean") end,
[ {"Testing the " ++ Contract ++ " contract",
fun() ->
?assertCmdOutput(Expected, filename:join(aeso_test_utils:contract_path(), Contract ++ "_test"))
end} || {Contract, Expected} <- contracts() ]}.
contracts() ->
[].
%% [{"voting",
%% "Delegate before vote\n"
%% "Cake: 1\n"
%% "Beer: 2\n"
%% "Winner: Beer\n"
%% "Delegate after vote\n"
%% "Cake: 1\n"
%% "Beer: 2\n"
%% "Winner: Beer\n"
%% }].
+77
View File
@@ -0,0 +1,77 @@
/* https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol
/*
The following is an extremely basic example of a solidity contract.
It takes a string upon creation and then repeats it when greet() is called.
*/
contract Greeter // The contract definition. A constructor of the same name will be automatically called on contract creation.
{
address creator; // At first, an empty "address"-type variable of the name "creator". Will be set in the constructor.
string greeting; // At first, an empty "string"-type variable of the name "greeting". Will be set in constructor and can be changed.
function Greeter(string _greeting) public // The constructor. It accepts a string input and saves it to the contract's "greeting" variable.
{
creator = msg.sender
greeting = _greeting
}
function greet() constant returns (string)
{
return greeting
}
function getBlockNumber() constant returns (uint) // this doesn't have anything to do with the act of greeting
{ // just demonstrating return of some global variable
return block.number
}
function setGreeting(string _newgreeting)
{
greeting = _newgreeting
}
/**********
Standard kill() function to recover funds
**********/
function kill()
{
if (msg.sender == creator) // only allow this action if the account sending the signal is the creator
suicide(creator); // kills this contract and sends remaining funds back to creator
}
}
*/
contract Greeter =
/* The creator of the contract will automatically
be set in creator() by the transaction creating the contract. */
function blockheight : unit => uint
record transaction = { tx : string }
record state = { greeting: string }
record retval = { state: state,
transactions: list(transaction)}
let state = { greeting = "Hello" }
let setGreeting =
(greeting: string) =>
state{ greeting = greeting }
/* this doesn't have anything to do with the act of greeting
just demonstrating return of some global variable */
function getBlockNumber() = blockheight()
/* There is no suicide functionallity in Sophia */
function kill() =
if ((caller() == creator()) /* only allow this action if the account sending the signal is the creator */
&& (balance() > 0)) /* only creata a transaction if there is something to send */
state{ transactions = [spend_tx(creator(), balance())] }
else state
+15
View File
@@ -0,0 +1,15 @@
## Requires ocaml >= 4.02, < 4.06
## and reason-3.0.0 (opam install reason).
default : voting_test
%.ml : %.re
refmt -p ml $< > $@
voting_test : rte.ml voting.ml voting_test.ml
ocamlopt -o $@ $^
clean :
rm -f *.cmi *.cmx *.ml *.o voting_test
+31
View File
@@ -0,0 +1,31 @@
// A simple test of the abort built-in function.
contract AbortTest =
record state = { value : int }
public function init(v : int) =
{ value = v }
// Aborting
public function do_abort(v : int, s : string) : () =
put_value(v)
revert_abort(s)
// Accessing the value
public function get_value() = state.value
public function put_value(v : int) = put(state{value = v})
public function get_values() : list(int) = [state.value]
public function put_values(v : int) = put(state{value = v})
// Some basic statistics
public function get_stats(acct : address) =
( Contract.balance, Chain.balance(acct) )
// Abort functions.
private function revert_abort(s : string) =
abort(s)
// This is still legal but will be stripped out.
// TODO: This function confuses the type inference, so it cannot be present.
//private function abort(s : string) = 42
+27
View File
@@ -0,0 +1,27 @@
contract Interface =
function do_abort : (int, string) => ()
function get_value : () => int
function put_value : (int) => ()
function get_values : () => list(int)
function put_values : (int) => ()
contract AbortTestInt =
record state = {r : Interface, value : int}
public function init(r : Interface, value : int) =
{r = r, value = value}
// Aborting
public function do_abort(v : int, s : string) =
put_value(v)
state.r.do_abort(v + 100, s)
// Accessing the value
public function put_value(v : int) = put(state{value = v})
public function get_value() = state.value
public function get_values() : list(int) =
state.value :: state.r.get_values()
public function put_values(v : int) =
put_value(v)
state.r.put_values(v + 1000)
+55
View File
@@ -0,0 +1,55 @@
// AENS tests
contract AENSTest =
// Name resolution
function resolve_word(name : string, key : string) : option(address) =
AENS.resolve(name, key)
function resolve_string(name : string, key : string) : option(string) =
AENS.resolve(name, key)
// Transactions
function preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash) : () = // Commitment hash
AENS.preclaim(addr, chash)
function signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
chash : hash, // Commitment hash
sign : signature) : () = // Signed by addr (if not Contract.address)
AENS.preclaim(addr, chash, signature = sign)
function claim(addr : address,
name : string,
salt : int) : () =
AENS.claim(addr, name, salt)
function signedClaim(addr : address,
name : string,
salt : int,
sign : signature) : () =
AENS.claim(addr, name, salt, signature = sign)
// TODO: update() -- how to handle pointers?
function transfer(owner : address,
new_owner : address,
name_hash : hash) : () =
AENS.transfer(owner, new_owner, name_hash)
function signedTransfer(owner : address,
new_owner : address,
name_hash : hash,
sign : signature) : () =
AENS.transfer(owner, new_owner, name_hash, signature = sign)
function revoke(owner : address,
name_hash : hash) : () =
AENS.revoke(owner, name_hash)
function signedRevoke(owner : address,
name_hash : hash,
sign : signature) : () =
AENS.revoke(owner, name_hash, signature = sign)
+145
View File
@@ -0,0 +1,145 @@
/*
contract AeToken {
function balanceOf(address addr) returns (uint256)
}
contract AEProof {
AeToken aeToken
mapping (bytes32 => Proof) private proofs
mapping (address => bytes32[]) private proofsByOwner
function AEProof(address tokenAddress) {
aeToken = AeToken(tokenAddress)
}
struct Proof {
address owner
uint timestamp
uint proofBlock
string comment
string ipfsHash
string document
}
function notarize(string document, string comment, string ipfsHash) onlyTokenHolder {
var proofHash = calculateHash(document)
var proof = proofs[proofHash]
require(proof.owner == address(0))
proof.owner = msg.sender
proof.timestamp = block.timestamp
proof.proofBlock = block.number
proof.comment = comment
proof.ipfsHash = ipfsHash
proof.document = document
proofsByOwner[msg.sender].push(proofHash)
}
function calculateHash(string document) constant returns (bytes32) {
return sha256(document)
}
function getProof(string document) constant returns (address owner, uint timestamp, uint proofBlock, string comment, string ipfsHash, string storedDocument) {
var calcHash = calculateHash(document)
var proof = proofs[calcHash]
require(proof.owner != address(0))
owner = proof.owner
timestamp = proof.timestamp
proofBlock = proof.proofBlock
comment = proof.comment
ipfsHash = proof.ipfsHash
storedDocument = proof.document
}
function getProofByHash(bytes32 hash) constant returns (address owner, uint timestamp, uint proofBlock, string comment, string ipfsHash, string storedDocument) {
var proof = proofs[hash]
require(proof.owner != address(0))
owner = proof.owner
timestamp = proof.timestamp
proofBlock = proof.proofBlock
comment = proof.comment
ipfsHash = proof.ipfsHash
storedDocument = proof.document
}
function hasProof(string document) constant returns (bool) {
var calcHash = calculateHash(document)
var storedProof = proofs[calcHash]
if (storedProof.owner == address(0)) {
return false
}
return true
}
function getProofsByOwner(address owner) constant returns (bytes32[]) {
return proofsByOwner[owner]
}
modifier onlyTokenHolder() {
uint balance = aeToken.balanceOf(msg.sender)
require(balance > 0)
_
}
}
*/
// No imports yet
// import contract aetoken
// fun balanceOf(addr : Address) : uint
contract AEProof =
record proof = { owner: address
, timestamp: uint
, proofBlock: uint
, comment: string
, ipfsHash: string
, document: string
}
record state = { aeToken : aetoken,
proofs : map(uint, proof),
proofsByOwner : map(address, array(uint)) }
function notarize(document:string, comment:string, ipfsHash:hash) =
let _ = require(aetoken.balanceOf(caller()) > 0)
let proofHash: uint = calculateHash(document)
let proof : proof = Map.get_(proofHash, state().proofs)
let _ = require(proof.owner == #0)
let proof' : proof = proof { owner = caller()
, timestamp = block().timestamp
, proofBlock = block().height
, comment = comment
, ipfsHash = ipfsHash
, document = document
}
state{ proofsByOwner = Map.insert(caller, proofHash, state.proofsByOwner),
proofs = Map.insert(proofHash, proof', state.proofs) }
function calculateHash(document: string) : uint = sha256(document)
function getProof(document) : proof =
let calcHash = calculateHash(document)
let proof = Map.get_(calcHash, state().proofs)
let _ = require(proof.owner != #0)
proof
function getProofByHash(hash: uint) : proof =
let proof = Map.get_(hash, state().proofs)
let _ = require(proof.owner != #0)
proof
function hasProof(document: string) : bool =
let calcHash = calculateHash(document)
let storedProof = Map.get_(calcHash, state().proofs)
storedProof.owner != #0
function getProofsByOwner(owner: address): array(uint) =
Map.get(owner, state())
function require(x : bool) : unit = if(x) () else abort("false")
+51
View File
@@ -0,0 +1,51 @@
// Try to cover all syntactic constructs.
contract AllSyntaxType =
type typeDecl /* bla */
type paramTypeDecl('a, 'b)
/** Multi-
* line
* comment
*/
function foo : _
contract AllSyntax =
type typeDecl = int
type paramTypeDecl('a, 'b) = (('a, 'b) => 'b) => list('a) => 'b => 'b
record nestedRecord = { x : int }
record recordType = { z : nestedRecord, y : int }
datatype variantType('a) = None | Some('a)
let valWithType : map(int, int) => option(int) = (m) => Map.get(m, 42)
let valNoType =
if(valWithType(Map.empty) == None)
print(42 mod 10 * 5 / 3)
function funWithType(x : int, y) : (int, list(int)) = (x, 0 :: [y] ++ [])
function funNoType() =
let foo = (x, y : bool) =>
if (! (y && x =< 0x0b || true)) [x]
else [11..20]
let setY(r : recordType) : unit = r{ y = 5 }
let setX(r : recordType, x : int) : recordType = r { z.x = x } // nested record update
let getY(r) = switch(r) {y = y} => y
switch (funWithType(1, -2))
(x, [y, z]) => bar({x = z, y = -y + - -z * (-1)})
(x, y :: _) => ()
function bitOperations(x, y) = bnot (0xff00 band x bsl 4 bxor 0xa5a5a5 bsr 4 bor y)
function mutual() =
let rec recFun(x : int) = mutFun(x)
and mutFun(x) = if(x =< 0) 1 else x * recFun(x - 1)
recFun(0)
let hash : address = #01ab0fff11
let b = false
let qcon = Mod.Con
let str = "blabla\nfoo"
let chr = '"'
+12
View File
@@ -0,0 +1,12 @@
// Compiler failed to detect the map lookup nested in the state update and
// generate the appropriate builtin for it.
contract BuiltinBug =
record state = {proofs : map(address, list(string))}
public function init() = {proofs = {}}
public stateful function createProof(hash : string) =
put( state{ proofs[Call.caller] = hash :: state.proofs[Call.caller] } )
+12
View File
@@ -0,0 +1,12 @@
contract TestContract =
record state = {
_allowed : map(address, map(address, int))}
public stateful function init() = {
_allowed = {}}
public stateful function approve(spender: address, value: int) : bool =
put(state{_allowed[Call.caller][spender] = value})
true
+13
View File
@@ -0,0 +1,13 @@
// Test more advanced chain interactions
contract Chain =
record state = { last_bf : address }
function init() : state =
{last_bf = Contract.address}
function miner() = Chain.coinbase
function save_coinbase() =
put(state{last_bf = Chain.coinbase})
+8
View File
@@ -0,0 +1,8 @@
contract ChannelEnv =
public function coinbase() : address = Chain.coinbase
public function timestamp() : int = Chain.timestamp
public function block_height() : int = Chain.block_height
public function difficulty() : int = Chain.difficulty
@@ -0,0 +1,7 @@
contract ChannelOnChainContractNameResolution =
public function can_resolve(name: string, key: string) : bool =
switch(AENS.resolve(name, key) : option(string))
None => false
Some(_address) => true
@@ -0,0 +1,48 @@
contract ChannelOnChainContractOracle =
type query_t = string
type answer_t = string
type oracle_id = oracle(query_t, answer_t)
type query_id = oracle_query(query_t, answer_t)
record state = { oracle : oracle_id,
question : string,
bets : map(string, address)
}
public function init(oracle: oracle_id, question: string) : state =
{ oracle = oracle,
question = question,
bets = {}
}
public stateful function place_bet(answer: string) =
switch(Map.lookup(answer, state.bets))
None =>
put(state{ bets = state.bets{[answer] = Call.caller}})
"ok"
Some(_value) =>
"bet_already_taken"
public function query_fee() =
Oracle.query_fee(state.oracle)
public function get_question(q: query_id) =
Oracle.get_question(state.oracle, q)
public stateful function resolve(q: query_id) =
switch(Oracle.get_answer(state.oracle, q))
None =>
"no response"
Some(result) =>
if(state.question == Oracle.get_question(state.oracle, q))
switch(Map.lookup(result, state.bets))
None =>
"no winning bet"
Some(winner) =>
Chain.spend(winner, Contract.balance)
"ok"
else
"different question"
@@ -0,0 +1,9 @@
contract Remote =
function get : () => int
function can_resolve : (string, string) => bool
contract RemoteCall =
function remote_resolve(r : Remote, name: string, key: string) : bool =
r.can_resolve(name, key)
+51
View File
@@ -0,0 +1,51 @@
contract Chess =
type board = map(int, map(int, string))
type state = board
private function get_row(r, m : board) =
Map.lookup_default(r, m, {})
private function set_piece(r, c, p, m : board) =
m { [r] = get_row(r, m) { [c] = p } }
private function get_piece(r, c, m : board) =
Map.lookup(c, get_row(r, m))
private function from_list(xs, m : board) =
switch(xs)
[] => m
(r, c, p) :: xs => from_list(xs, set_piece(r, c, p, m))
function init() =
from_list([ (2, 1, "white pawn"), (7, 1, "black pawn")
, (2, 2, "white pawn"), (7, 2, "black pawn")
, (2, 3, "white pawn"), (7, 3, "black pawn")
, (2, 4, "white pawn"), (7, 4, "black pawn")
, (2, 5, "white pawn"), (7, 5, "black pawn")
, (2, 6, "white pawn"), (7, 6, "black pawn")
, (2, 7, "white pawn"), (7, 7, "black pawn")
, (2, 8, "white pawn"), (7, 8, "black pawn")
, (1, 1, "white rook"), (8, 1, "black rook")
, (1, 2, "white knight"), (8, 2, "black knight")
, (1, 3, "white bishop"), (8, 3, "black bishop")
, (1, 4, "white queen"), (8, 4, "black queen")
, (1, 5, "white king"), (8, 5, "black king")
, (1, 6, "white bishop"), (8, 6, "black bishop")
, (1, 7, "white knight"), (8, 7, "black knight")
, (1, 8, "white rook"), (8, 8, "black rook")
], {})
function piece(r, c) = get_piece(r, c, state)
function move_piece(r, c, r1, c1) =
switch(piece(r, c))
Some(p) => put(set_piece(r1, c1, p, state))
function destroy_piece(r, c) =
put(state{ [r] = Map.delete(c, get_row(r, state)) })
function delete_row(r) =
put(Map.delete(r, state))
+86
View File
@@ -0,0 +1,86 @@
contract Remote =
function up_to : (int) => list(int)
function sum : (list(int)) => int
function some_string : () => string
function pair : (int, string) => (int, string)
function squares : (int) => list((int, int))
function filter_some : (list(option(int))) => list(int)
function all_some : (list(option(int))) => option(list(int))
contract ComplexTypes =
record state = { worker : Remote }
function init(worker) = {worker = worker}
function sum_acc(xs, n) =
switch(xs)
[] => n
x :: xs => sum_acc(xs, x + n)
// Sum a list of integers
function sum(xs : list(int)) =
sum_acc(xs, 0)
function up_to_acc(n, xs) =
switch(n)
0 => xs
_ => up_to_acc(n - 1, n :: xs)
function up_to(n) = up_to_acc(n, [])
record answer('a) = {label : string, result : 'a}
function remote_triangle(worker, n) : answer(int) =
let xs = worker.up_to(gas = 100000, n)
let t = worker.sum(xs)
{ label = "answer:", result = t }
function remote_list(n) : list(int) =
state.worker.up_to(n)
function some_string() = "string"
function remote_string() : string =
state.worker.some_string()
function pair(x : int, y : string) = (x, y)
function remote_pair(n : int, s : string) : (int, string) =
state.worker.pair(gas = 10000, n, s)
function map(f, xs) =
switch(xs)
[] => []
x :: xs => f(x) :: map(f, xs)
function squares(n) =
map((i) => (i, i * i), up_to(n))
function remote_squares(n) : list((int, int)) =
state.worker.squares(n)
// option types
function filter_some(xs : list(option(int))) : list(int) =
switch(xs)
[] => []
None :: ys => filter_some(ys)
Some(x) :: ys => x :: filter_some(ys)
function remote_filter_some(xs : list(option(int))) : list(int) =
state.worker.filter_some(xs)
function all_some(xs : list(option(int))) : option(list(int)) =
switch(xs)
[] => Some([])
None :: ys => None
Some(x) :: ys =>
switch(all_some(ys))
Some(xs) => Some(x :: xs)
None => None
function remote_all_some(xs : list(option(int))) : option(list(int)) =
state.worker.all_some(gas = 10000, xs)
+20
View File
@@ -0,0 +1,20 @@
contract OtherContract =
function multiply : (int, int) => int
contract ThisContract =
record state = { server : OtherContract, n : int }
function init(server : OtherContract) =
{ server = server, n = 2 }
function square() =
put(state{ n @ n = state.server.multiply(value = 100, n, n) })
function get_n() = state.n
function tip_server() =
Chain.spend(state.server.address, Call.value)
+9
View File
@@ -0,0 +1,9 @@
contract Counter =
record state = { value : int }
function init(val) = { value = val }
function get() = state.value
function tick() = put(state{ value = state.value + 1 })
+43
View File
@@ -0,0 +1,43 @@
//
// Dutch auction example
//
contract DutchAuction =
record state = { start_amount : int,
start_height : int,
dec : int,
beneficiary : address,
sold : bool }
// Add to work around current lack of predefined functions
private function spend(to, amount) =
let total = Contract.balance
Chain.spend(to, amount)
total - amount
private function require(b : bool, err : string) =
if(!b) abort(err)
// TTL set by user on posting contract, typically (start - end ) div dec
public function init(beneficiary, start, decrease) : state =
require(start > 0 && decrease > 0, "bad args")
{ start_amount = start,
start_height = Chain.block_height,
beneficiary = beneficiary,
dec = decrease,
sold = false }
// -- API
// We are the buyer... interesting case to buy for someone else and keep 10%
public stateful function bid() =
require( !(state.sold), "sold")
let cost =
state.start_amount - (Chain.block_height - state.start_height) * state.dec
require( Contract.balance >= cost, "no money")
// transaction(SpendTx({recipient = state.beneficiary,
// amount = cost })) // or self.balance ** burn money **
spend(state.beneficiary, cost)
spend(Call.caller, Contract.balance)
put(state{sold = true})
+69
View File
@@ -0,0 +1,69 @@
// Testing primitives for accessing the block chain environment
contract Interface =
function contract_address : () => address
function call_origin : () => address
function call_caller : () => address
function call_value : () => int
contract Environment =
record state = {remote : Interface}
function init(remote) = {remote = remote}
function set_remote(remote) = put({remote = remote})
// -- Information about the this contract ---
// Address
function contract_address() : address = Contract.address
function nested_address(who) : address =
who.contract_address(gas = 1000)
// Balance
function contract_balance() : int = Contract.balance
// -- Information about the current call ---
// Origin
function call_origin() : address = Call.origin
function nested_origin() : address =
state.remote.call_origin()
// Caller
function call_caller() : address = Call.caller
function nested_caller() : address =
state.remote.call_caller()
// Value
function call_value() : int = Call.value
function nested_value(value : int) : int =
state.remote.call_value(value = value / 2)
// Gas price
function call_gas_price() : int = Call.gas_price
// -- Information about the chain ---
// Account balances
function get_balance(acct : address) : int = Chain.balance(acct)
// Block hash
function block_hash(height : int) : int = Chain.block_hash(height)
// Coinbase
function coinbase() : address = Chain.coinbase
// Block timestamp
function timestamp() : int = Chain.timestamp
// Block height
function block_height() : int = Chain.block_height
// Difficulty
function difficulty() : int = Chain.difficulty
// Gas limit
function gas_limit() : int = Chain.gas_limit
+90
View File
@@ -0,0 +1,90 @@
contract ERC20Token =
record state = {
totalSupply : int,
decimals : int,
name : string,
symbol : string,
balances : map(address, int),
allowed : map(address, map(address,int)),
// Logs, remove when native Events are there
transfer_log : list((address,address,int)),
approval_log : list((address,address,int))}
// init(100000000, 10, "Token Name", "TKN")
public stateful function init(_totalSupply : int, _decimals : int, _name : string, _symbol : string ) = {
totalSupply = _totalSupply,
decimals = _decimals,
name = _name,
symbol = _symbol,
balances = {[Call.caller] = _totalSupply }, // creator gets all Tokens
allowed = {},
// Logs, remove when native Events are there
transfer_log = [],
approval_log = []}
public stateful function totalSupply() : int = state.totalSupply
public stateful function decimals() : int = state.decimals
public stateful function name() : string = state.name
public stateful function symbol() : string = state.symbol
public stateful function balanceOf(tokenOwner : address ) : int =
Map.lookup_default(tokenOwner, state.balances, 0)
public stateful function transfer(to : address, tokens : int) =
put( state{balances[Call.caller] = sub(state.balances[Call.caller], tokens) })
put( state{balances[to] = add(Map.lookup_default(to, state.balances, 0), tokens) })
transferEvent(Call.caller, to, tokens)
true
public stateful function approve(spender : address, tokens : int) =
// allowed[Call.caller] field must have a value!
ensure_allowed(Call.caller)
put( state{allowed[Call.caller][spender] = tokens} )
approvalEvent(Call.caller, spender, tokens)
true
public stateful function transferFrom(from : address, to : address, tokens : int) =
put( state{ balances[from] = sub(state.balances[from], tokens) })
put( state{ allowed[from][Call.caller] = sub(state.allowed[from][Call.caller], tokens) })
put( state{ balances[to] = add(balanceOf(to), tokens) })
transferEvent(from, to, tokens)
true
public function allowance(_owner : address, _spender : address) : int =
state.allowed[_owner][_spender]
public stateful function getTransferLog() : list((address,address,int)) =
state.transfer_log
public stateful function getApprovalLog() : list((address,address,int)) =
state.approval_log
//
// Private Functions
//
private function ensure_allowed(key : address) =
switch(Map.lookup(key, state.allowed))
None => put(state{allowed[key] = {}})
Some(_) => ()
private function transferEvent(from : address, to : address, tokens : int) =
let e = (from, to, tokens)
put( state{transfer_log = e :: state.transfer_log })
e
private function approvalEvent(from : address, to : address, tokens : int) =
let e = (from, to, tokens)
put( state{approval_log = e :: state.approval_log })
e
private function require(b : bool, err : string) =
if(!b) abort(err)
private function sub(_a : int, _b : int) : int =
require(_b =< _a, "Error")
_a - _b
private function add(_a : int, _b : int) : int =
let c : int = _a + _b
require(c >= _a, "Error")
c
+22
View File
@@ -0,0 +1,22 @@
contract Events =
type alias_int = int
type alias_address = address
type alias_string = string
datatype event =
Event1(indexed alias_int, indexed int, string)
| Event2(alias_string, indexed alias_address)
// | BadEvent1(indexed string, string)
// | BadEvent2(indexed int, int)
function f1(x : int, y : string) =
Chain.event(Event1(x, x+1, y))
function f2(s : string) =
Chain.event(Event2(s, Call.caller))
function f3(x : int) =
Chain.event(Event1(x, x + 2, Int.to_str(x + 7)))
function i2s(i : int) = Int.to_str(i)
function a2s(a : address) = Address.to_str(a)
+6
View File
@@ -0,0 +1,6 @@
contract Exploits =
// We'll hack the bytecode of this changing the return type to string.
function pair(n : int) = (n, 0)
+17
View File
@@ -0,0 +1,17 @@
// An implementation of the factorial function where each recursive
// call is to another contract. Not the cheapest way to compute factorial.
contract FactorialServer =
function fac : (int) => int
contract Factorial =
record state = {worker : FactorialServer}
function init(worker) = {worker = worker}
function set_worker(worker) = put(state{worker = worker})
function fac(x : int) : int =
if(x == 0) 1
else x * state.worker.fac(x - 1)
+65
View File
@@ -0,0 +1,65 @@
/*
* A simple crowd-funding example
*/
contract FundMe =
record spend_args = { recipient : address,
amount : int }
record state = { contributions : map(address, int),
total : int,
beneficiary : address,
deadline : int,
goal : int }
private function require(b : bool, err : string) =
if(!b) abort(err)
private function spend(args : spend_args) =
Chain.spend(args.recipient, args.amount)
public function init(beneficiary, deadline, goal) : state =
{ contributions = {},
beneficiary = beneficiary,
deadline = deadline,
total = 0,
goal = goal }
private function is_contributor(addr) =
Map.member(addr, state.contributions)
public stateful function contribute() =
if(Chain.block_height >= state.deadline)
spend({ recipient = Call.caller, amount = Call.value }) // Refund money
false
else
let amount =
Map.lookup_default(Call.caller, state.contributions, 0) + Call.value
put(state{ contributions[Call.caller] = amount,
total @ tot = tot + Call.value })
true
public stateful function withdraw() =
if(Chain.block_height < state.deadline)
abort("Cannot withdraw before deadline")
if(Call.caller == state.beneficiary)
withdraw_beneficiary()
elif(is_contributor(Call.caller))
withdraw_contributor()
else
abort("Not a contributor or beneficiary")
private stateful function withdraw_beneficiary() =
require(state.total >= state.goal, "Project was not funded")
spend({recipient = state.beneficiary,
amount = Contract.balance })
put(state{ beneficiary = #0 })
private stateful function withdraw_contributor() =
if(state.total >= state.goal)
abort("Project was funded")
let to = Call.caller
spend({recipient = to,
amount = state.contributions[to]})
put(state{ contributions @ c = Map.delete(to, c) })
+3
View File
@@ -0,0 +1,3 @@
contract Identity =
function main (x:int) = x
+9
View File
@@ -0,0 +1,9 @@
contract Remote =
function missing : (int) => int
contract Init_error =
record state = {value : int}
function init(r : Remote, x : int) =
{value = r.missing(x)}
+36
View File
@@ -0,0 +1,36 @@
contract MapOfMaps =
type board = map(int, map(int, string))
type map2('a, 'b, 'c) = map('a, map('b, 'c))
record state = { big1 : map2(string, string, string),
big2 : map2(string, string, string),
small1 : map(string, string),
small2 : map(string, string) }
private function empty_state() =
{ big1 = {}, big2 = {},
small1 = {}, small2 = {} }
function init() = empty_state()
function setup_state() =
let small = {["key"] = "val"}
put({ big1 = {["one"] = small},
big2 = {["two"] = small},
small1 = small,
small2 = small })
// -- Garbage collection of inner map when outer map is garbage collected
function test1_setup() =
let inner = {["key"] = "val"}
put(empty_state() { big1 = {["one"] = inner} })
function test1_execute() =
put(state{ big1 = {} })
function test1_check() =
state.big1
+100
View File
@@ -0,0 +1,100 @@
contract Maps =
record pt = {x : int, y : int}
record state = { map_i : map(int, pt),
map_s : map(string, pt) }
function init() = { map_i = {}, map_s = {} }
function get_state() = state
// {[k] = v}
function map_i() =
{ [1] = {x = 1, y = 2},
[2] = {x = 3, y = 4},
[3] = {x = 5, y = 6} }
function map_s() =
{ ["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() })
// m[k]
function get_i(k, m : map(int, pt)) = m[k]
function get_s(k, m : map(string, pt)) = m[k]
function get_state_i(k) = get_i(k, state.map_i)
function get_state_s(k) = get_s(k, state.map_s)
// m[k = v]
function get_def_i(k, v, m : map(int, pt)) = m[k = v]
function get_def_s(k, v, m : map(string, pt)) = m[k = v]
function get_def_state_i(k, v) = get_def_i(k, v, state.map_i)
function get_def_state_s(k, v) = get_def_s(k, v, state.map_s)
// 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) })
// 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 })
// 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 })
// m{[k = def] @ x = v }
function addx_def_i(k, v, d, m : map(int, pt)) = m{ [k = v].x @ x = x + d }
function addx_def_s(k, v, d, m : map(string, pt)) = m{ [k = v].x @ x = x + d }
// Map.member
function member_i(k, m : map(int, pt)) = Map.member(k, m)
function member_s(k, m : map(string, pt)) = Map.member(k, m)
function member_state_i(k) = member_i(k, state.map_i)
function member_state_s(k) = member_s(k, state.map_s)
// Map.lookup
function lookup_i(k, m : map(int, pt)) = Map.lookup(k, m)
function lookup_s(k, m : map(string, pt)) = Map.lookup(k, m)
function lookup_state_i(k) = lookup_i(k, state.map_i)
function lookup_state_s(k) = lookup_s(k, state.map_s)
// Map.lookup_default
function lookup_def_i(k, m : map(int, pt), def : pt) =
Map.lookup_default(k, m, def)
function lookup_def_s(k, m : map(string, pt), def : pt) =
Map.lookup_default(k, m, def)
function lookup_def_state_i(k, def) = lookup_def_i(k, state.map_i, def)
function lookup_def_state_s(k, def) = lookup_def_s(k, state.map_s, def)
// 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) })
// Map.size
function size_i(m : map(int, pt)) = Map.size(m)
function size_s(m : map(string, pt)) = Map.size(m)
function size_state_i() = size_i(state.map_i)
function size_state_s() = size_s(state.map_s)
// Map.to_list
function tolist_i(m : map(int, pt)) = Map.to_list(m)
function tolist_s(m : map(string, pt)) = Map.to_list(m)
function tolist_state_i() = tolist_i(state.map_i)
function tolist_state_s() = tolist_s(state.map_s)
// 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) })
+29
View File
@@ -0,0 +1,29 @@
contract MapUpdater =
function update_map : (int, string, map(int, string)) => map(int, string)
contract Benchmark =
record state = { updater : MapUpdater,
map : map(int, string) }
function init(u, m) = { updater = u, map = m }
function set_updater(u) = put(state{ updater = u })
function update_map(k : int, v : string, m) = m{ [k] = v }
function update(a : int, b : int, v : string) =
if (a > b) ()
else
put(state{ map[a] = v })
update(a + 1, b, v)
function get(k) = state.map[k]
function noop() = ()
function benchmark(k, v) =
let m = state.updater.update_map(k, v, state.map)
put(state{ map = m })
m
+6
View File
@@ -0,0 +1,6 @@
contract MinimalInit =
record state = {foo : int}
function init() =
{ foo = 0 }
+123
View File
@@ -0,0 +1,123 @@
/* Multi-signature wallet from
https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol
*/
contract MultiSig =
record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint }
datatype event =
Confirmation (address, hash) // of { .owner : Address, .operation : Hash }
| Revoke (address, hash) // of { .owner : Address, .operation : Hash }
| OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address }
| OwnerAdded (address) // of { .newOwner : Address }
| OwnerRemoved (address) // of { .removedOwner : Address }
| ReqChanged (uint) // of { .newReq : uint }
let maxOwners : uint = 250
record state = { nRequired : uint
, nOwners : uint
, owners : map(uint, address)
, ownerIndex : map(address, uint)
, pending : map(hash, pending_state)
, pendingIndex : list(address) }
function init (owners : list(address), nRequired : uint) : state =
let n = length(owners) + 1
{ nRequired = nRequired,
nOwners = n,
owners = Map.from_list(List.zip([1..n], caller() :: owners)),
ownerIndex = Map.from_list(List.zip(caller() :: owners, [1..n])) }
function lookup(map, key) =
switch(Map.get(key, map))
None => abort("not found")
Some(value) => value
function revoke(operation : hash) =
let ownerIx = lookup(state.ownerIndex, caller())
let pending = lookup(state.pendingIndex, operation)
let ownerIxBit = 1 bsl (ownerIx - 1)
let _ = require(pending.ownersDone band ownerIxBit > 0)
let pending' = pending { yetNeeded = pending.yetNeeded + 1
, ownersDone = pending.ownersDone - ownerIxBit }
put(state{ pendingIndex.operator = pending' })
event(Revoke(caller, operation))
datatype check_pending = CheckOk(state) | CheckFail(state)
function changeOwner(fromOwner : address, toOwner : address) =
switch(check_pending(callhash()))
CheckFail(state') => { state = state' }
CheckOk(state') =>
if(isOwner(toOwner)) put(state')
else
switch(Map.get(fromOwner, state.ownerIndex))
None => { state = state' }
Some(ownerIx) =>
{ state = state' { owners = Map.insert(ownerIx, toOwner, state'.owners)
, ownerIndex = Map.delete(fromOwner, Map.insert(toOwner, ownerIx, state'.ownerIndex))
, pending = Map.empty
, pendingIx = [] },
events = [OwnerChanged(fromOwner, toOwner)] }
function addOwner(newOwner : address) =
let _ = require (!isOwner(newOwner))
switch(check_pending(callhash()))
CheckFail(state') => { state = state' }
CheckOk(state') =>
if(state.nOwners >= maxOwners) () /* TODO */
else
let nOwners' = state'.nOwners + 1
{ state = state' { owners = Map.insert(nOwners', newOwner, state'.owners)
, ownerIndex = Map.insert(newOwner, nOwners', state'.ownerIndex)
, pending = Map.empty
, pendingIx = [] },
event = [OwnerAdded(newOwner)] }
function removeOwner(oldOwner : address) =
let _ = require(isOwner(oldOwner))
let _ = require(state.nRequired > state.nOwners - 1)
switch(check_pending(callhash()))
CheckFail(state') => { state = state' }
CheckOk(state') =>
let ownerIx = lookup(state'.ownerIndex, oldOwner)
{ state = state' { owners = Map.delete(ownerIx, state'.owners)
, ownerIndex = Map.delete(newOwner, state'.ownerIndex)
, pending = Map.empty
, pendingIx = [] },
event = [OwnerRemoved(oldOwner)] }
function changeRequirement(newReq : uint) =
let _ = require(newReq =< state.nOwners)
switch(check_pending(callhash()))
CheckFail(state') => { state = state' }
CheckOk(state') =>
{ state = state' { nRequired = newReq
, pending = Map.empty
, pendingIx = [] },
event = [ReqChanged(newReq)] }
function getOwner(ownerIx0 : uint) =
lookup(state.owners, ownerIx0 + 1)
function isOwner(owner : address) =
switch(Map.get(owner, state.ownerIndex))
None => false
Some(_) => true
function hasConfirmed(operation : hash, owner : address) =
switch(Map.get(operation, state.pending))
None => false
Some(pending) =>
let _ = require(isOwner(owner))
let ownerIx = lookup(state.ownerIndex, owner)
let ownerIxBit = 1 bsl (ownerIx - 1)
(pending.ownersDone band ownerIxBit) != 0
/* Leave the rest for now... */
+7
View File
@@ -0,0 +1,7 @@
contract MultiplicationServer =
function multiply(x : int, y : int) =
switch(Call.value >= 100)
true => x * y
+16
View File
@@ -0,0 +1,16 @@
contract NameClash =
function double_proto : () => int
function double_proto : () => int
function proto_and_def : int => int
function proto_and_def(n) = n + 1
function double_def(x) = x
function double_def(y) = 0
// abort, put and state are builtin
function abort() : int = 0
function put(x) = x
function state(x, y) = x + y
+44
View File
@@ -0,0 +1,44 @@
// - + * / mod arithmetic operators
// bnot band bor bxor bsl bsr bitwise operators
// ! && || logical operators
// == != < > =< >= comparison operators
// :: ++ list operators
contract Operators =
function int_op(a : int, b : int, op : string) =
switch(op)
"+" => a + b
"-" => a - b
"*" => a * b
"/" => a / b
"mod" => a mod b
"^" => a ^ b
"bnot" => bnot a
"band" => a band b
"bor" => a bor b
"bxor" => a bxor b
"bsl" => a bsl b
"bsr" => a bsr b
function bool_op(a : bool, b : bool, op : string) =
switch(op)
"!" => !a
"&&" => a && b
"||" => a || b
function cmp_op(a : int, b : int, op : string) =
switch(op)
"==" => a == b
"!=" => a != b
"<" => a < b
">" => a > b
"=<" => a =< b
">=" => a >= b
function cons(a, l) = a :: l
function concat(l1, l2) = l1 ++ l2
function hash(s) = // String.sha3(s)
let x = String.sha3(s)
let y = String.sha3(s)
(x, y)
+112
View File
@@ -0,0 +1,112 @@
contract Oracles =
type fee = int
type ttl = Chain.ttl
type query_t = string
type answer_t = int
type oracle_id = oracle(query_t, answer_t)
type query_id = oracle_query(query_t, answer_t)
function registerOracle(acct : address,
qfee : fee,
ttl : ttl) : oracle_id =
Oracle.register(acct, qfee, ttl)
function registerIntIntOracle(acct : address,
qfee : fee,
ttl : ttl) : oracle(int, int) =
Oracle.register(acct, qfee, ttl)
function registerStringStringOracle(acct : address,
qfee : fee,
ttl : ttl) : oracle(string, string) =
Oracle.register(acct, qfee, ttl)
function signedRegisterOracle(acct : address,
sign : signature,
qfee : fee,
ttl : ttl) : oracle_id =
Oracle.register(acct, qfee, ttl, signature = sign)
function queryFee(o : oracle_id) : fee =
Oracle.query_fee(o)
function createQuery(o : oracle_id,
q : query_t,
qfee : fee,
qttl : ttl,
rttl : ttl) : query_id =
require(qfee =< Call.value, "insufficient value for qfee")
Oracle.query(o, q, qfee, qttl, rttl)
// Do not use in production!
function unsafeCreateQuery(o : oracle_id,
q : query_t,
qfee : fee,
qttl : ttl,
rttl : ttl) : query_id =
Oracle.query(o, q, qfee, qttl, rttl)
// Do not use in production!
function unsafeCreateQueryThenErr(o : oracle_id,
q : query_t,
qfee : fee,
qttl : ttl,
rttl : ttl) : query_id =
let res = Oracle.query(o, q, qfee, qttl, rttl)
require(qfee >= 100000000000000000, "causing a late error")
res
function extendOracle(o : oracle_id,
ttl : ttl) : () =
Oracle.extend(o, ttl)
function signedExtendOracle(o : oracle_id,
sign : signature, // Signed oracle address
ttl : ttl) : () =
Oracle.extend(o, signature = sign, ttl)
function respond(o : oracle_id,
q : query_id,
r : answer_t) : () =
Oracle.respond(o, q, r)
function signedRespond(o : oracle_id,
q : query_id,
sign : signature,
r : answer_t) : () =
Oracle.respond(o, q, signature = sign, r)
function getQuestion(o : oracle_id,
q : query_id) : query_t =
Oracle.get_question(o, q)
function hasAnswer(o : oracle_id,
q : query_id) =
switch(Oracle.get_answer(o, q))
None => false
Some(_) => true
function getAnswer(o : oracle_id,
q : query_id) : option(answer_t) =
Oracle.get_answer(o, q)
datatype complexQuestion = Why(int) | How(string)
datatype complexAnswer = NoAnswer | Answer(complexQuestion, string, int)
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) =
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)
Oracle.get_answer(o, q)
private function require(b : bool, err : string) =
if(!b) abort(err)
+11
View File
@@ -0,0 +1,11 @@
contract OraclesErr =
public function unsafeCreateQueryThenErr(
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
let x = Oracle.query(o, q, qfee, qttl, rttl)
switch(0) 1 => ()
x // Never reached.
+24
View File
@@ -0,0 +1,24 @@
contract OraclesGas =
type fee = int
type question_t = string
type answer_t = int
public function happyPathWithAllBuiltinsAtSameHeight(
qfee : fee,
ottl : Chain.ttl,
ettl : Chain.ttl,
qttl : Chain.ttl,
rttl : Chain.ttl
) =
let question = "why"
let answer = 42
let o = Oracle.register(Contract.address, qfee, ottl) : oracle(question_t, answer_t)
Oracle.extend(o, ettl)
require(qfee =< Call.value, "insufficient value for qfee")
let q = Oracle.query(o, question, qfee, qttl, rttl)
Oracle.respond(o, q, answer)
()
private function require(b : bool, err : string) =
if(!b) abort(err)
+37
View File
@@ -0,0 +1,37 @@
contract Oracles =
type fee = int
type ttl = Chain.ttl
type query_t = string
type answer_t = string
type oracle_id = oracle(query_t, answer_t)
type query_id = oracle_query(query_t, answer_t)
function createQuery(o : oracle_id,
q : query_t,
qfee : fee,
qttl : ttl,
rttl : ttl) : query_id =
require(qfee =< Call.value, "insufficient value for qfee")
Oracle.query(o, q, qfee, qttl, rttl)
function respond(o : oracle_id,
q : query_id,
sign : signature,
r : answer_t) : () =
Oracle.respond(o, q, signature = sign, r)
function getQuestion(o : oracle_id,
q : query_id) : query_t =
Oracle.get_question(o, q)
function getAnswer(o : oracle_id,
q : query_id) : option(answer_t) =
Oracle.get_answer(o, q)
private function require(b : bool, err : string) =
if(!b) abort(err)
+16
View File
@@ -0,0 +1,16 @@
contract Identity =
function zip_with(f, xs, ys) =
switch((xs, ys))
(x :: xs, y :: ys) => f(x, y) :: zip_with(f, xs, ys)
_ => []
// Check that we can use zip_with at different types
function foo() =
zip_with((x, y) => x + y, [1, 2, 3], [4, 5, 6, 7])
function bar() =
zip_with((x, y) => if(x) y else 0, [true, false, true, false], [1, 2, 3])
+57
View File
@@ -0,0 +1,57 @@
contract MapServer =
function insert : (string, string, map(string, string)) => map(string, string)
function delete : (string, map(string, string)) => map(string, string)
contract PrimitiveMaps =
record state = { remote : MapServer,
map : map(string, string),
map2 : map(string, string) }
function init(r) =
let m = {}
{ remote = r, map = m, map2 = m }
function set_remote(r) = put(state{ remote = r })
function insert(k, v, m) : map(string, string) = m{ [k] = v }
function delete(k, m) : map(string, string) = Map.delete(k, m)
function remote_insert(k, v, m) =
state.remote.insert(k, v, m)
function remote_delete(k, m) =
state.remote.delete(k, m)
function get_state_map() = state.map
function set_state_map(m) = put(state{ map = m })
function clone_state() = put(state{ map2 = state.map })
function insert_state(k, v) = put(state{ map @ m = m { [k] = v } })
function delete_state(k) = put(state{ map @ m = Map.delete(k, m) })
function lookup_state(k) = Map.lookup(k, state.map)
function double_insert_state(k, v1, v2) =
put(state{ map @ m = m { [k] = v1 },
map2 @ m = m { [k] = v2 } })
function test() =
let m = {} : map(string, string)
let m1 = m { ["foo"] = "value_of_foo",
["bla"] = "value_of_bla" }
let m2 = Map.delete("foo", m1)
let m3 = m2 { ["bla"] = "new_value_of_bla" }
[Map.lookup("foo", m), Map.lookup("bla", m),
Map.lookup("foo", m1), Map.lookup("bla", m1),
Map.lookup("foo", m2), Map.lookup("bla", m2),
Map.lookup("foo", m3), Map.lookup("bla", m3)]
function return_map() =
Map.delete("goo", {["foo"] = "bar", ["goo"] = "gaa"})
function argument_map(m : map(string, string)) =
m["foo"]
+60
View File
@@ -0,0 +1,60 @@
/* Primitive types */
type address = string;
type uint = int;
exception Abort;
type env =
{ mutable _caller : address
};
let env = { _caller: "" };
/* Builtin functions */
let caller() : address = env._caller;
let abort() = raise(Abort);
let call(who, fn) = {
env._caller = who;
let res = try(fn()) { | e => env._caller = ""; raise(e) };
env._caller = "";
res
};
/* Library functions */
let require(x) = x ? () : abort();
/* -- Managing contract state ------------------------------------------------ */
type state_rep('s) = { mutable state : option('s) };
let newStateRep() : state_rep('s) = { state: None };
exception UninitializedState;
exception ReinitializedState;
let getState(rep) =
switch(rep.state) {
| None => raise(UninitializedState)
| Some(s) => s
};
let setState(rep, s) =
switch(rep.state) {
| None => rep.state = Some(s)
| Some(_) => raise(ReinitializedState)
};
module type Contract = {
type state;
type args;
let init : args => state;
let stateRep : state_rep(state);
};
module Setup = (C : Contract) => {
let init(creator, args) =
setState(C.stateRep, call(creator, () => C.init(args)));
let reset() = C.stateRep.state = None;
};
+126
View File
@@ -0,0 +1,126 @@
open Rte;
/* Contract type */
module type Voting = {
type state;
type args = list(string);
let stateRep : state_rep(state);
let init : args => state;
let giveRightToVote : address => unit;
let delegate : address => unit;
let vote : int => unit;
let winnerName : unit => string;
let currentTally : unit => list((string, int));
};
/* Contract implementation */
module Voting : Voting = {
/* Not so nice */
module AddrKey = { type t = address; let compare = Pervasives.compare };
module AddrMap = Map.Make(AddrKey);
type addr_map('a) = AddrMap.t('a);
/* Types */
type proposal =
{ name: string
, mutable voteCount: uint
};
type voter =
{ mutable weight: int
, mutable delegate: option(address)
, mutable vote: option(int)
};
type state =
{ chairPerson: address
, mutable voters: addr_map(voter)
, proposals: list(proposal)
};
/* Initialization */
type args = list(string);
let init(proposalNames: args): state =
{ chairPerson: caller(),
voters: AddrMap.empty,
proposals: List.map((name) => {name: name, voteCount: 0}, proposalNames)
};
/* Boilerplate */
let stateRep = newStateRep();
let state() = getState(stateRep);
let initVoter() = { weight: 1, delegate: None, vote: None };
let giveRightToVote(voter: address) = {
require(caller() == state().chairPerson);
require(!AddrMap.mem(voter, state().voters));
state().voters = AddrMap.add(voter, initVoter(), state().voters);
()
};
let rec delegateChain(delegate: address) = {
require(delegate != caller()); /* Delegation loop! */
let voter = AddrMap.find(delegate, state().voters);
switch(voter.delegate) {
| None => delegate;
| Some(d) => delegateChain(d)
}
};
let addVote(candidate, weight) = {
let proposal = List.nth(state().proposals, candidate);
proposal.voteCount = proposal.voteCount + weight;
};
let delegateVote(delegateTo: address, weight: uint) = {
let voter = AddrMap.find(delegateTo, state().voters);
switch(voter.vote) {
| Some(vote) => addVote(vote, weight)
| None => voter.weight = voter.weight + weight
}
};
let delegate(delegateTo: address) = {
require(delegateTo != caller());
let voter = AddrMap.find(caller(), state().voters);
require(voter.vote == None);
let finalDelegate = delegateChain(delegateTo);
voter.vote = Some(0); /* Hm... */
voter.delegate = Some(finalDelegate);
delegateVote(finalDelegate, voter.weight)
};
let vote(candidate: uint) = {
let voter = AddrMap.find(caller(), state().voters);
require(voter.vote == None);
voter.vote = Some(candidate);
addVote(candidate, voter.weight);
};
let rec winningProposal'(current, rest) =
switch(rest) {
| [] => current;
| [p, ...ps] => winningProposal'(p.voteCount > current.voteCount ? p : current, ps)
};
/* const */
let winningProposal() : proposal = {
switch(state().proposals) {
| [] => abort()
| [p, ...ps] => winningProposal'(p, ps)
}
};
/* const */
let winnerName() = winningProposal().name;
/* const */
let currentTally() =
List.map((p) => (p.name, p.voteCount), state().proposals);
}
+42
View File
@@ -0,0 +1,42 @@
open Rte;
open Voting;
let creator = "0x123";
let voter1 = "0x1001";
let voter2 = "0x1002";
let voter3 = "0x1003";
let other = "0xffff";
module SetupVote = Setup(Voting);
open Voting;
let print_tally() = {
let tally = call(other, () => currentTally());
List.map(((name, count)) => Printf.printf("%s: %d\n", name, count), tally);
let winner = call(other, () => winnerName());
Printf.printf("Winner: %s\n", winner);
};
Printf.printf("Delegate before vote\n");
SetupVote.init(creator, ["Cake", "Beer"]);
call(creator, () => giveRightToVote(voter1));
call(creator, () => giveRightToVote(voter2));
call(creator, () => giveRightToVote(voter3));
call(voter3, () => delegate(voter1));
call(voter1, () => vote(1));
call(voter2, () => vote(0));
print_tally();
SetupVote.reset();
Printf.printf("Delegate after vote\n");
SetupVote.init(creator, ["Cake", "Beer"]);
call(creator, () => giveRightToVote(voter1));
call(creator, () => giveRightToVote(voter2));
call(creator, () => giveRightToVote(voter3));
call(voter1, () => vote(1));
call(voter3, () => delegate(voter1));
call(voter2, () => vote(0));
print_tally();
+27
View File
@@ -0,0 +1,27 @@
contract Remote1 =
function main : (int) => int
contract Remote2 =
function call : (Remote1, int) => int
contract Remote3 =
function get : () => int
function tick : () => ()
contract RemoteCall =
function call(r : Remote1, x : int) : int =
r.main(gas = 10000, value = 10, x)
function staged_call(r1 : Remote1, r2 : Remote2, x : int) =
r2.call(r1, x)
function increment(r3 : Remote3) =
r3.tick()
function get(r3 : Remote3) =
r3.get()
function plus(x, y) = x + y
+20
View File
@@ -0,0 +1,20 @@
contract Remote1 =
function set : (int) => int
contract RemoteCall =
record state = { i : int }
function init(x) = { i = x }
function set( x : int) : int =
let old = state.i
put(state{ i = x })
old
function call(r : Remote1, x : int, g : int) : int =
r.set(gas = g, value = 10, x)
function get() = state.i
+99
View File
@@ -0,0 +1,99 @@
contract Oracles =
function registerOracle :
(address,
int,
Chain.ttl) => oracle(string, int)
function createQuery :
(oracle(string, int),
string,
int,
Chain.ttl,
Chain.ttl) => oracle_query(string, int)
function unsafeCreateQuery :
(oracle(string, int),
string,
int,
Chain.ttl,
Chain.ttl) => oracle_query(string, int)
function respond :
(oracle(string, int),
oracle_query(string, int),
int) => ()
contract OraclesErr =
function unsafeCreateQueryThenErr :
(oracle(string, int),
string,
int,
Chain.ttl,
Chain.ttl) => oracle_query(string, int)
contract RemoteOracles =
public function callRegisterOracle(
r : Oracles,
acct : address,
qfee : int,
ttl : Chain.ttl) : oracle(string, int) =
r.registerOracle(acct, qfee, ttl)
public function callCreateQuery(
r : Oracles,
value : int,
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
require(value =< Call.value, "insufficient value")
r.createQuery(value = value, o, q, qfee, qttl, rttl)
// Do not use in production!
public function callUnsafeCreateQuery(
r : Oracles,
value : int,
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
r.unsafeCreateQuery(value = value, o, q, qfee, qttl, rttl)
// Do not use in production!
public function callUnsafeCreateQueryThenErr(
r : OraclesErr,
value : int,
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
r.unsafeCreateQueryThenErr(value = value, o, q, qfee, qttl, rttl)
// Do not use in production!
public function callUnsafeCreateQueryAndThenErr(
r : Oracles,
value : int,
o : oracle(string, int),
q : string,
qfee : int,
qttl : Chain.ttl,
rttl : Chain.ttl) : oracle_query(string, int) =
let x = r.unsafeCreateQuery(value = value, o, q, qfee, qttl, rttl)
switch(0) 1 => ()
x // Never reached.
public function callRespond(
r : Oracles,
o : oracle(string, int),
q : oracle_query(string, int),
qr : int) =
r.respond(o, q, qr)
private function require(b : bool, err : string) =
if(!b) abort(err)
+23
View File
@@ -0,0 +1,23 @@
contract RemoteState =
record rstate = { i : int, s : string, m : map(int, int) }
function look_at(s : rstate) = ()
function return_s(big : bool) =
let x = "short"
let y = "______longer_string_at_least_32_bytes_long___________longer_string_at_least_32_bytes_long___________longer_string_at_least_32_bytes_long_____"
if(big) y else x
function return_m(big : bool) =
let x = { [1] = 2 }
let y = { [1] = 2, [3] = 4, [5] = 6 }
if(big) y else x
function get(s : rstate) = s
function get_i(s : rstate) = s.i
function get_s(s : rstate) = s.s
function get_m(s : rstate) = s.m
function fun_update_i(s : rstate, ni) = s{ i = ni }
function fun_update_s(s : rstate, ns) = s{ s = ns }
function fun_update_m(s : rstate, nm) = s{ m = nm }
function fun_update_mk(s : rstate, k, v) = s{ m = s.m{[k] = v} }
+22
View File
@@ -0,0 +1,22 @@
contract Remote =
function id : ('a) => 'a
function missing : ('a) => 'a
function wrong_type : (string) => string
contract Main =
function id(x : int) =
x
function wrong_type(x : int) =
x
function remote_id(r : Remote, x) =
r.id(x)
function remote_missing(r : Remote, x) =
r.missing(x)
function remote_wrong_type(r : Remote, x) =
r.wrong_type(x)
+21
View File
@@ -0,0 +1,21 @@
contract ValueOnErr =
function err : () => int
function ok : () => int
contract RemoteValueOnErr =
public function callErr(
r : ValueOnErr,
value : int) : int =
r.err(value = value)
public function callErrLimitGas(
r : ValueOnErr,
value : int,
gas : int) : int =
r.err(value = value, gas = gas)
public function callOk(
r : ValueOnErr,
value : int) : int =
r.ok(value = value)
+3
View File
@@ -0,0 +1,3 @@
contract Simple =
type t = int => int
+29
View File
@@ -0,0 +1,29 @@
/* Example from Solidity by Example
http://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html
The Solidity code:
contract SimpleStorage {
uint storedData
function set(uint x) {
storedData = x
}
function get() constant returns (uint) {
return storedData
}
}
*/
contract SimpleStorage =
type event = int
record state = { data : int }
function init(value : int) : state = { data = value }
function get() : int = state.data
function set(value : int) =
put(state{data = value})
+26
View File
@@ -0,0 +1,26 @@
contract SpendContract =
function withdraw : (int) => int
contract SpendTest =
function spend(to, amount) =
let total = Contract.balance
Chain.spend(to, amount)
total - amount
function withdraw(amount) : int =
spend(Call.caller, amount)
function withdraw_from(account, amount) =
account.withdraw(amount)
withdraw(amount)
function spend_from(from, to, amount) =
from.withdraw(amount)
Chain.spend(to, amount)
Chain.balance(to)
function get_balance() = Contract.balance
function get_balance_of(a) = Chain.balance(a)
+29
View File
@@ -0,0 +1,29 @@
// Testing more interesting state types
contract Stack =
type stack('a) = list('a)
record state = { stack : stack(string),
size : int }
function init(ss : list(string)) = { stack = ss, size = length(ss) }
private function length(xs) =
switch(xs)
[] => 0
_ :: xs => length(xs) + 1
stateful function pop() : string =
switch(state.stack)
s :: ss =>
put(state{ stack = ss, size = state.size - 1 })
s
stateful function push(s) =
put(state{ stack = s :: state.stack, size = state.size + 1 })
state.size
function all() = state.stack
function size() = state.size
+67
View File
@@ -0,0 +1,67 @@
contract Remote =
function look_at : (state) => ()
function return_s : (bool) => string
function return_m : (bool) => map(int, int)
function get : (state) => state
function get_i : (state) => int
function get_s : (state) => string
function get_m : (state) => map(int, int)
function fun_update_i : (state, int) => state
function fun_update_s : (state, string) => state
function fun_update_m : (state, map(int, int)) => state
function fun_update_mk : (state, int, int) => state
contract StateHandling =
record state = { i : int, s : string, m : map(int, int) }
function init(r : Remote, i : int) =
let state0 = { i = 0, s = "undefined", m = {} }
r.fun_update_i(state0, i)
function read() = state
function read_i() = state.i
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 })
function pass_it(r : Remote) = r.look_at(state)
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)
function return_it_m(r : Remote, big : bool) =
let x = r.return_m(big)
Map.size(x)
function pass(r : Remote) = r.get(state)
function pass_i(r : Remote) = r.get_i(state)
function pass_s(r : Remote) = r.get_s(state)
function pass_m(r : Remote) = r.get_m(state)
function pass_update_i(r : Remote, i) = r.fun_update_i(state, i)
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))
// remote called
function look_at(s : state) = ()
function get(s : state) = s
function get_i(s : state) = s.i
function get_s(s : state) = s.s
function get_m(s : state) = s.m
function fun_update_i(st, ni) = st{ i = ni }
function fun_update_s(st, ns) = st{ s = ns }
function fun_update_m(st, nm) = st{ m = nm }
function fun_update_mk(st, k, v) = st{ m = st.m{[k] = v} }
+4
View File
@@ -0,0 +1,4 @@
contract Strings =
function str_len(s) = String.length(s)
function str_concat(s1, s2) = String.concat(s1, s2)
+101
View File
@@ -0,0 +1,101 @@
contract Identity =
// type xy = {x:int, y:int}
// type xz = {x:int, z:int}
// type yz = {y:int, z:int}
record point = {x:int,y:int}
record cp('a) = {color: string, p:'a}
//type intpoint = point(int)
// //if (x==42) 1 else (x*x)
// }
//let baz() = {age:3, name:(4:int)}
//let foo(a,b,c) = c
// let rec fac(n) = if((n:int)==0) 1 else (n*fac(n-1))
// and main(x) = x::[x+1]
// let lentr(l) = lent(0,l)
// let rec len(l) =
// switch(l) {
// | [] => 0
// | x::xs => 1+len(xs)
// }
// let lent(n,l) =
// switch (l) {
// | [] => n
// | (x::xs) => lent(n+1,xs)
// }
// let rec app(a,b) =
// switch(a) {
// | [] => b
// | (x::xs) => x::app(xs,b)
// }
// let rec revt(l,r) =
// switch(l) {
// | [] => r
// | x::xs => revt(xs,x::r)
// }
// let rev(l) = revt(l,[])
// let main(x:int) = {
// switch(rev([1,2,3])) {
// | h::_ => h
// }
// }
//let fac(n:int) = {
// if (n==0) 1 else (n*fac(n-1))
//}
//let main(x) = switch((12,34)) {
//| (13,_) => x
//| (_,a) => x+a
// | y => y+1
// }
//let main(x) = ({y:0>1, x:x==0}:point(bool))
//let main(x) = x
//let main(x) = len(1::2::[])
//let main(x) = ((x,x):list('a))
// let main(x) = switch("a") {
// | "b" => 0
// | "a" => 1
// | "c" => 2
// }
//let main(x) = x.color+1
//let main(x) = switch(({x:x, y:x+1}:cp(int))) {
// | {y:xx} => xx
// }
//let main(x) = {x:0, y:1, z:2}
// let id(x) = x
// let double(x) = x+x
// let pair(x) = (1,2)
// let unit(x) = ()
// let tuples(x) = ((1,x),(2,3,4))
// let singleton(x) = [x]
// let rec seq(n) = if (n==0) [] else (app(seq(n-1),[n]))
// let idString(s:string) = s
// let pairString(s:string) = (s,s)
// let revStrings(ss:list(string))=rev(ss)
// let makePoint(x,y) = {x:x, y:y}
// let getx(x) = x.x
// let updatex(p,x) = p{x:x}
// let quad(x) = {let y=x+x; let z=y+y; z;}
// let noblock(x) = {x; x}
// let unit(x) = ()
// let foo(x) = switch (x) {
// | y => y+1
// }
// let p(x) = {color:"blue", p:{x:x, y:x+1}}
//let twice(f,x) = f(f(x))
// let twice(f,x) = f(f(x))
// let double(x) = x+x
// let main(x) = twice((y=>y+y),x)
// let rec map(f,xs) = switch(xs) {
// | [] => []
// | (x::ys) => f(x)::map(f,ys)
// }
// let id(x) = x
// let main(xs) = map(double,xs)
function z(f,x) = x
private function s(n) = (f,x)=>f(n(f,x))
private function add(m,n) = (f,x)=>m(f,n(f,x))
function main(_) =
let three=s(s(s(z)))
add(three,three)
(((i)=>i+1),0)
+42
View File
@@ -0,0 +1,42 @@
contract Test =
record r = { x : map(string, string), y : int }
record r' = { y : string }
record r2 = { z : int, w : int }
record r3 = { x : int, z : int }
function set_x(r : r, z) = r{ x["foo"] @ x = x + 1 }
function bla(m : map(string, int)) = { [0] = "bla", ["foo"] = "" }
function foo(r) = r { y = 0 }
function bar() = { y = "foo", z = 0 }
function baz() = { y = "foo", w = 0 }
function foo1() = zz
function test1() : string = { y = 0 }
function test2(x : string) = x { y = 0 }
function test3(x : string) = x { y @ y = y + 1 }
function test4(x : string) : int = x.y
function test5(xs) =
switch(xs)
x :: x => x
[] => 0
function case_pat(xs) =
switch(xs)
[] => 0
x :: xs => "x"
function foo2(m : map(string, int)) = m{ [1] = "bla" }
function bad_if(x, y : int, w : int, z : string) =
if(x) y
elif(x) w
else z
function type_error(r, x) =
set_x(set_x(x, r), x)
+6
View File
@@ -0,0 +1,6 @@
contract UpfrontCharges =
record state = { b : int } // For enabling retrieval of sender balance observed inside init.
public function init() : state = { b = b() }
public function initialSenderBalance() : int = state.b
public function senderBalance() : int = b()
private function b() = Chain.balance(Call.origin)
+7
View File
@@ -0,0 +1,7 @@
contract ValueOnErr =
public function err() : int =
switch(0) 1 => 5
public function ok() : int =
11
+29
View File
@@ -0,0 +1,29 @@
contract VariantTypes =
datatype state = Started(started_state) | Stopped
record started_state = {owner : address, balance : int, color : color}
datatype color = Red | Green | Blue | Grey(int)
function init() = Stopped
function require(b) = if(!b) abort("required")
function start(bal : int) =
switch(state)
Stopped => put(Started({owner = Call.caller, balance = bal, color = Grey(0)}))
function stop() =
switch(state)
Started(st) =>
require(Call.caller == st.owner)
put(Stopped)
st.balance
function get_color() = switch(state) Started(st) => st.color
function set_color(c) = switch(state) Started(st) => put(Started(st{color = c}))
function get_state() = state
+97
View File
@@ -0,0 +1,97 @@
/* Contract type */
contract VotingType =
type state
function init : list(string) => state
function giveRightToVote : address => unit
function delegate : address => unit
function vote : int => unit
function winnerName : unit => string
function currentTally : unit => list((string, int))
/* Contract implementation */
contract Voting =
// Types
record proposal =
{ name : string
, voteCount : uint
}
datatype vote = NotVoted | Voted(int) | Delegated(address)
record voter =
{ weight : int
, vote : vote
}
record state =
{ chairPerson : address
, voters : map(address, voter)
, proposals : list(proposal)
}
// Initialization
function init(proposalNames: list(string)): state =
{ chairPerson = caller(),
voters = Map.empty,
proposals = List.map((name) => {name = name, voteCount = 0}, proposalNames) }
function initVoter() = { weight = 1, vote = NotVoted}
function giveRightToVote(voter: address) =
require(caller() == state.chairPerson)
require(!Map.mem(voter, state.voters))
put(state{ voters = Map.add(voter, initVoter(), state.voters) })
function delegateChain(delegate: address) =
require(delegate != caller()) /* Delegation loop! */
let voter = Map.find(delegate, state.voters)
switch(voter.vote)
Delegated(d) => delegateChain(d)
_ => delegate
function addVote(candidate, weight) =
let proposal = List.nth(state.proposals, candidate)
proposal{ voteCount = proposal.voteCount + weight }
function delegateVote(delegateTo: address, weight: uint) =
let voter = Map.find(delegateTo, state.voters)
switch(voter.vote)
Voted(vote) => addVote(vote, weight)
Delegated(_) => abort("impossible") // impossible
NotVoted => voter{ weight = voter.weight + weight }
function delegate(delegateTo: address) =
require(delegateTo != caller())
let voter = Map.find(caller(), state.voters)
require(voter.vote == NotVoted)
let finalDelegate = delegateChain(delegateTo)
let voter' = voter{ vote = Delegated(finalDelegate) }
delegateVote(finalDelegate, voter.weight)
function vote(candidate: uint) =
let voter = Map.find(caller(), state.voters)
require(voter.vote == NotVoted)
let voter' = voter{ vote = Voted(candidate) }
addVote(candidate, voter.weight)
function winningProposal'(current, rest) =
switch(rest)
[] => current
p :: ps => winningProposal'(if (p.voteCount > current.voteCount) p else current, ps)
// const
function winningProposal() : proposal =
switch(state.proposals)
[] => abort("none")
p :: ps => winningProposal'(p, ps)
// const
function winnerName() = winningProposal().name
// const
function currentTally() =
List.map((p) => (p.name, p.voteCount), state.proposals)
+56
View File
@@ -0,0 +1,56 @@
/* Example from Solidity by Example
http://solidity.readthedocs.io/en/develop/common-patterns.html
contract WithdrawalContract {
address public richest
uint public mostSent
mapping (address => uint) pendingWithdrawals
function WithdrawalContract() payable {
richest = msg.sender
mostSent = msg.value
}
function becomeRichest() payable returns (bool) {
if (msg.value > mostSent) {
pendingWithdrawals[richest] += msg.value
richest = msg.sender
mostSent = msg.value
return true
} else {
return false
}
}
function withdraw() {
uint amount = pendingWithdrawals[msg.sender]
// Remember to zero the pending refund before
// sending to prevent re-entrancy attacks
pendingWithdrawals[msg.sender] = 0
msg.sender.transfer(amount)
}
}
*/
contract WithdrawalContract =
record state = { richest : address,
mostSent : uint,
pendingWithdrawals : map(address, uint) }
function becomeRichest() : result(bool) =
if (call().value > state.mostSent)
let totalAmount : uint = Map.get_(state.richest, pendingWithdrawals) + call().value
{state = state{ pendingWithdrawals = Map.insert(state.richest, call().value, state.pendingWithdrawals),
richest = call().sender,
mostSent = call().value },
result = true}
else
{result = false}
function withdraw() =
let amount : uint = Map.get_(call().sender, state.pendingWithdrawals)
{ state.pendingWithdrawals = Map.insert(call().sender, 0, state.pendingWithdrawals),
transactions = spend_tx(amount, call().sender) }