diff --git a/src/passes/operators/operators.ml b/src/passes/operators/operators.ml index 94ec09373..456c0172a 100644 --- a/src/passes/operators/operators.ml +++ b/src/passes/operators/operators.ml @@ -148,6 +148,7 @@ module Simplify = struct module Cameligo = struct let constants = function | "assert" -> ok C_ASSERTION + | "chain_id" -> ok C_CHAIN_ID | "Current.balance" -> ok C_BALANCE | "balance" -> ok C_BALANCE | "Current.time" -> ok C_NOW diff --git a/src/test/contracts/multisig.mligo b/src/test/contracts/multisig.mligo new file mode 100644 index 000000000..2a7753cc6 --- /dev/null +++ b/src/test/contracts/multisig.mligo @@ -0,0 +1,66 @@ +// storage type + +type counter = nat +type threshold = nat +type authorized_keys = (key) list +type id = string + +type storage = { + id : id; + counter : counter; + threshold : threshold; + auth : authorized_keys +} + +// I/O types + +type message = unit -> (operation) list + +type signatures = (key_hash*signature) list +type check_message_pt = { + counter : counter; + message : message; + signatures : signatures +} + +type return = (operation) list * storage + +type parameter = CheckMessage of check_message_pt + +let check_message (param,s : check_message_pt * storage) : return = + let message : message = param.message in + let s = + if (param.counter <> s.counter) then + let coco = failwith ("Counters does not match") in s + else ( + let packed_payload : bytes = Bytes.pack ((message, param.counter, s.id, chain_id)) in + let valid : nat = 0n in + let keys : authorized_keys = s.auth in + let aux = fun (vk, pkh_sig: (nat* authorized_keys) * (key_hash*signature)) -> + let (valid,keys) = vk in + match (keys) with + | [] -> (valid,keys) + | key::tl -> ( + let keys = tl in + if (pkh_sig.0 = Crypto.hash_key (key)) then + let valid = + if (Crypto.check key pkh_sig.1 packed_payload) then valid + 1n + else let coco = failwith ("Invalid signature") in valid + in + (valid,keys) + else (valid,keys) + ) + in + let (valid,keys) = List.fold aux param.signatures (valid,keys) in + if (valid < s.threshold) then + let coco = failwith ("Not enough signatures passed the check") in + s + else + {s with counter = s.counter + 1n} + ) + in + (message(unit),s) + +let main (param, s: parameter * storage) : return = + match (param) with + | CheckMessage (p) -> check_message (p,s) diff --git a/src/test/contracts/multisig.religo b/src/test/contracts/multisig.religo new file mode 100644 index 000000000..a2d709ab9 --- /dev/null +++ b/src/test/contracts/multisig.religo @@ -0,0 +1,80 @@ +// storage type + +type counter = nat; +type threshold = nat; +type authorized_keys = list (key); +type id = string; + +type storage = { + id : id, + counter : counter, + threshold : threshold, + auth : authorized_keys +}; + +// I/O types + +type message = unit => list (operation); +type dummy = (key_hash,signature); +type signatures = list (dummy); + +type check_message_pt = { + counter : counter, + message : message, + signatures : signatures +}; + +type return = (list (operation),storage); + +type parameter = CheckMessage (check_message_pt); + +let check_message = ((param,s) : (check_message_pt, storage)) : return => { + let message : message = param.message; + let s = + if (param.counter != s.counter) { + let coco = failwith ("Counters does not match"); + s; + } + else { + let packed_payload : bytes = Bytes.pack ((message, param.counter, s.id, chain_id)); + let valid : nat = 0n; + let keys : authorized_keys = s.auth; + let aux = ((vk,pkh_sig) : ((nat, authorized_keys),(key_hash,signature))):(nat,authorized_keys) => { + let (valid,keys) = vk; + switch(keys) { + | [] => (valid,keys); + | [key, ...tl] => { + let keys = tl; + if (pkh_sig[0] == Crypto.hash_key (key)){ + let valid = + if (Crypto.check (key, pkh_sig[1], packed_payload)){ + valid + 1n ; + } + else { + let coco = failwith ("Invalid signature"); + valid; + }; + (valid,keys); + } + else { + (valid,keys); + }; + }; + }; + }; + let (valid,keys) = List.fold (aux, param.signatures, (valid,keys)); + if (valid < s.threshold) { + let coco = failwith ("Not enough signatures passed the check"); + s; + } + else { + {...s,counter : s.counter + 1n}; + }; + }; + (message(unit),s) +}; + +let main = ((param, s) : (parameter,storage)) : return => + switch(param) { + | CheckMessage (p) => check_message ((p,s)) + } diff --git a/src/test/multisig_tests.ml b/src/test/multisig_tests.ml index de6fbaaa4..e71c5ce99 100644 --- a/src/test/multisig_tests.ml +++ b/src/test/multisig_tests.ml @@ -1,24 +1,27 @@ open Trace open Test_helpers -let type_file f = - let%bind simplified = Ligo.Compile.Of_source.compile f (Syntax_name "pascaligo") in +let file = "./contracts/multisig.ligo" +let mfile = "./contracts/multisig.mligo" +let refile = "./contracts/multisig.religo" + +let type_file f s = + let%bind simplified = Ligo.Compile.Of_source.compile f (Syntax_name s) in let%bind typed,state = Ligo.Compile.Of_simplified.compile simplified in ok @@ (typed,state) -let get_program = +let get_program f st = let s = ref None in fun () -> match !s with | Some s -> ok s | None -> ( - let%bind program = type_file "./contracts/multisig.ligo" in + let%bind program = type_file f st in s := Some program ; ok program ) -let compile_main () = - let%bind simplified = Ligo.Compile.Of_source.compile "./contracts/multisig.ligo" (Syntax_name "pascaligo") in - let%bind typed_prg,_ = Ligo.Compile.Of_simplified.compile simplified in +let compile_main f s () = + let%bind typed_prg,_ = type_file f s in let%bind mini_c_prg = Ligo.Compile.Of_typed.compile typed_prg in let%bind michelson_prg = Ligo.Compile.Of_mini_c.aggregate_and_compile_contract mini_c_prg "main" in let%bind (_contract: Tezos_utils.Michelson.michelson) = @@ -51,8 +54,8 @@ let chain_id_zero = e_chain_id @@ Tezos_crypto.Base58.simple_encode Tezos_base__TzPervasives.Chain_id.zero (* sign the message 'msg' with 'keys', if 'is_valid'=false the providid signature will be incorrect *) -let params counter msg keys is_validl = - let%bind program,_ = get_program () in +let params counter msg keys is_validl f s = + let%bind program,_ = get_program f s () in let aux = fun acc (key,is_valid) -> let (_,_pk,sk) = key in let (pkh,_,_) = str_keys key in @@ -73,42 +76,42 @@ let params counter msg keys is_validl = ]) (* Provide one valid signature when the threshold is two of two keys *) -let not_enough_1_of_2 () = - let%bind program,_ = get_program () in +let not_enough_1_of_2 f s () = + let%bind program,_ = get_program f s () in let exp_failwith = "Not enough signatures passed the check" in let keys = gen_keys () in - let%bind test_params = params 0 empty_message [keys] [true] in + let%bind test_params = params 0 empty_message [keys] [true] f s in let%bind () = expect_string_failwith program "main" (e_pair test_params (init_storage 2 0 [keys;gen_keys()])) exp_failwith in ok () -let unmatching_counter () = - let%bind program,_ = get_program () in +let unmatching_counter f s () = + let%bind program,_ = get_program f s () in let exp_failwith = "Counters does not match" in let keys = gen_keys () in - let%bind test_params = params 1 empty_message [keys] [true] in + let%bind test_params = params 1 empty_message [keys] [true] f s in let%bind () = expect_string_failwith program "main" (e_pair test_params (init_storage 1 0 [keys])) exp_failwith in ok () (* Provide one invalid signature (correct key but incorrect signature) when the threshold is one of one key *) -let invalid_1_of_1 () = - let%bind program,_ = get_program () in +let invalid_1_of_1 f s () = + let%bind program,_ = get_program f s () in let exp_failwith = "Invalid signature" in let keys = [gen_keys ()] in - let%bind test_params = params 0 empty_message keys [false] in + let%bind test_params = params 0 empty_message keys [false] f s in let%bind () = expect_string_failwith program "main" (e_pair test_params (init_storage 1 0 keys)) exp_failwith in ok () (* Provide one valid signature when the threshold is one of one key *) -let valid_1_of_1 () = - let%bind program,_ = get_program () in +let valid_1_of_1 f s () = + let%bind program,_ = get_program f s () in let keys = gen_keys () in let%bind () = expect_eq_n_trace_aux [0;1;2] program "main" (fun n -> - let%bind params = params n empty_message [keys] [true] in + let%bind params = params n empty_message [keys] [true] f s in ok @@ e_pair params (init_storage 1 n [keys]) ) (fun n -> @@ -117,13 +120,13 @@ let valid_1_of_1 () = ok () (* Provive two valid signatures when the threshold is two of three keys *) -let valid_2_of_3 () = - let%bind program,_ = get_program () in +let valid_2_of_3 f s () = + let%bind program,_ = get_program f s () in let param_keys = [gen_keys (); gen_keys ()] in let st_keys = param_keys @ [gen_keys ()] in let%bind () = expect_eq_n_trace_aux [0;1;2] program "main" (fun n -> - let%bind params = params n empty_message param_keys [true;true] in + let%bind params = params n empty_message param_keys [true;true] f s in ok @@ e_pair params (init_storage 2 n st_keys) ) (fun n -> @@ -132,36 +135,52 @@ let valid_2_of_3 () = ok () (* Provide one invalid signature and two valid signatures when the threshold is two of three keys *) -let invalid_3_of_3 () = - let%bind program,_ = get_program () in +let invalid_3_of_3 f s () = + let%bind program,_ = get_program f s () in let valid_keys = [gen_keys() ; gen_keys()] in let invalid_key = gen_keys () in let param_keys = valid_keys @ [invalid_key] in let st_keys = valid_keys @ [gen_keys ()] in - let%bind test_params = params 0 empty_message param_keys [false;true;true] in + let%bind test_params = params 0 empty_message param_keys [false;true;true] f s in let exp_failwith = "Invalid signature" in let%bind () = expect_string_failwith program "main" (e_pair test_params (init_storage 2 0 st_keys)) exp_failwith in ok () (* Provide two valid signatures when the threshold is three of three keys *) -let not_enough_2_of_3 () = - let%bind program,_ = get_program () in +let not_enough_2_of_3 f s () = + let%bind program,_ = get_program f s() in let valid_keys = [gen_keys() ; gen_keys()] in let st_keys = gen_keys () :: valid_keys in - let%bind test_params = params 0 empty_message (valid_keys) [true;true] in + let%bind test_params = params 0 empty_message (valid_keys) [true;true] f s in let exp_failwith = "Not enough signatures passed the check" in let%bind () = expect_string_failwith program "main" (e_pair test_params (init_storage 3 0 st_keys)) exp_failwith in ok () let main = test_suite "Multisig" [ - test "compile" compile_main ; - test "unmatching_counter" unmatching_counter ; - test "valid_1_of_1" valid_1_of_1 ; - test "invalid_1_of_1" invalid_1_of_1 ; - test "not_enough_signature" not_enough_1_of_2 ; - test "valid_2_of_3" valid_2_of_3 ; - test "invalid_3_of_3" invalid_3_of_3 ; - test "not_enough_2_of_3" not_enough_2_of_3 ; + test "compile" (compile_main file "pascaligo"); + test "unmatching_counter" (unmatching_counter file "pascaligo"); + test "valid_1_of_1" (valid_1_of_1 file "pascaligo"); + test "invalid_1_of_1" (invalid_1_of_1 file "pascaligo"); + test "not_enough_signature" (not_enough_1_of_2 file "pascaligo"); + test "valid_2_of_3" (valid_2_of_3 file "pascaligo"); + test "invalid_3_of_3" (invalid_3_of_3 file "pascaligo"); + test "not_enough_2_of_3" (not_enough_2_of_3 file "pascaligo"); + test "compile (mligo)" (compile_main mfile "cameligo"); + test "unmatching_counter (mligo)" (unmatching_counter mfile "cameligo"); + test "valid_1_of_1 (mligo)" (valid_1_of_1 mfile "cameligo"); + test "invalid_1_of_1 (mligo)" (invalid_1_of_1 mfile "cameligo"); + test "not_enough_signature (mligo)" (not_enough_1_of_2 mfile "cameligo"); + test "valid_2_of_3 (mligo)" (valid_2_of_3 mfile "cameligo"); + test "invalid_3_of_3 (mligo)" (invalid_3_of_3 mfile "cameligo"); + test "not_enough_2_of_3 (mligo)" (not_enough_2_of_3 mfile "cameligo"); + test "compile (religo)" (compile_main refile "reasonligo"); + test "unmatching_counter (religo)" (unmatching_counter refile "reasonligo"); + test "valid_1_of_1 (religo)" (valid_1_of_1 refile "reasonligo"); + test "invalid_1_of_1 (religo)" (invalid_1_of_1 refile "reasonligo"); + test "not_enough_signature (religo)" (not_enough_1_of_2 refile "reasonligo"); + test "valid_2_of_3 (religo)" (valid_2_of_3 refile "reasonligo"); + test "invalid_3_of_3 (religo)" (invalid_3_of_3 refile "reasonligo"); + test "not_enough_2_of_3 (religo)" (not_enough_2_of_3 refile "reasonligo"); ]