diff --git a/src/proto_alpha/lib_protocol/test/activation.ml b/src/proto_alpha/lib_protocol/test/activation.ml new file mode 100644 index 000000000..fa5807b72 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/activation.ml @@ -0,0 +1,350 @@ +(**************************************************************************) +(* *) +(* Copyright (c) 2014 - 2018. *) +(* Dynamic Ledger Solutions, Inc. *) +(* *) +(* All rights reserved. No warranty, explicit or implicit, provided. *) +(* *) +(**************************************************************************) + +(** The activation operation creates an implicit contract from a + registered commitment present in the context. It is parametrized by + a public key hash (pkh) and a secret. + + The commitments are composed of : + - half a pkh ; + - a blinded pkh that can be revealed by the secret ; + - an amount. + + The commitments and the secrets are generated from + /scripts/create_genesis/create_genenis.py and should be coherent. +*) + +open Proto_alpha +open Alpha_context +open Test_utils +open Test_tez + +(* Generated commitments and secrets *) + +let commitments = + List.map (fun (bpkh, a) -> + Commitment_repr.{ + blinded_public_key_hash=Blinded_public_key_hash.of_b58check_exn bpkh ; + amount = Tez_repr.of_mutez_exn (Int64.of_string a)} + ) + [ ( "btz1bRL4X5BWo2Fj4EsBdUwexXqgTf75uf1qa", "23932454669343" ) ; + ( "btz1SxjV1syBgftgKy721czKi3arVkVwYUFSv", "72954577464032" ) ; + ( "btz1LtoNCjiW23txBTenALaf5H6NKF1L3c1gw", "217487035428349" ) ; + ( "btz1SUd3mMhEBcWudrn8u361MVAec4WYCcFoy", "4092742372031" ) ; + ( "btz1MvBXf4orko1tsGmzkjLbpYSgnwUjEe81r", "17590039016550" ) ; + ( "btz1LoDZ3zsjgG3k3cqTpUMc9bsXbchu9qMXT", "26322312350555" ) ; + ( "btz1RMfq456hFV5AeDiZcQuZhoMv2dMpb9hpP", "244951387881443" ) ; + ( "btz1Y9roTh4A7PsMBkp8AgdVFrqUDNaBE59y1", "80065050465525" ) ; + ( "btz1Q1N2ePwhVw5ED3aaRVek6EBzYs1GDkSVD", "3569618927693" ) ; + ( "btz1VFFVsVMYHd5WfaDTAt92BeQYGK8Ri4eLy", "9034781424478" ) ; + ] + +type secret_account = { + account : public_key_hash ; + activation_code : Blinded_public_key_hash.activation_code ; + amount : Tez.t ; +} + +let secrets () = + (* Exported from proto_alpha client - TODO : remove when relocated to lib_crypto *) + let read_key mnemonic email password = + match Bip39.of_words mnemonic with + | None -> assert false + | Some t -> + (* TODO: unicode normalization (NFKD)... *) + let sk = Bip39.to_seed ~passphrase:(email ^ password) t in + let sk = Cstruct.(to_bigarray (sub sk 0 32)) in + let sk : Signature.Secret_key.t = + Ed25519 (Data_encoding.Binary.of_bytes_exn Ed25519.Secret_key.encoding sk) in + let pk = Signature.Secret_key.to_public_key sk in + let pkh = Signature.Public_key.hash pk in + (pkh, pk, sk) + in + List.map (fun (mnemonic, secret, amount, pkh, password, email) -> + let (pkh', pk, sk) = read_key mnemonic email password in + let pkh = Signature.Public_key_hash.of_b58check_exn pkh in + assert (Signature.Public_key_hash.equal pkh pkh'); + let account = Account.{ pkh ; pk ; sk } in + Account.add_account account ; + { account = account.pkh ; + activation_code = Blinded_public_key_hash.activation_code_of_hex secret ; + amount = Option.unopt_exn (Invalid_argument "tez conversion") + (Tez.of_mutez (Int64.of_string amount)) + }) + [ + (["envelope"; "hospital"; "mind"; "sunset"; "cancel"; "muscle"; "leisure"; + "thumb"; "wine"; "market"; "exit"; "lucky"; "style"; "picnic"; "success"], + "0f39ed0b656509c2ecec4771712d9cddefe2afac", + "23932454669343", + "tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF", + "z0eZHQQGKt", + "cjgfoqmk.wpxnvnup@tezos.example.org" + ); + (["flag"; "quote"; "will"; "valley"; "mouse"; "chat"; "hold"; "prosper"; + "silk"; "tent"; "cruel"; "cause"; "demise"; "bottom"; "practice"], + "41f98b15efc63fa893d61d7d6eee4a2ce9427ac4", + "72954577464032", + "tz1X4maqF9tC1Yn4jULjHRAyzjAtc25Z68TX", + "MHErskWPE6", + "oklmcktr.ztljnpzc@tezos.example.org" + ); + (["library"; "away"; "inside"; "paper"; "wise"; "focus"; "sweet"; "expose"; + "require"; "change"; "stove"; "planet"; "zone"; "reflect"; "finger"], + "411dfef031eeecc506de71c9df9f8e44297cf5ba", + "217487035428349", + "tz1SWBY7rWMutEuWS54Pt33MkzAS6eWkUuTc", + "0AO6BzQNfN", + "ctgnkvqm.kvtiybky@tezos.example.org" + ); + (["cruel"; "fluid"; "damage"; "demand"; "mimic"; "above"; "village"; "alpha"; + "vendor"; "staff"; "absent"; "uniform"; "fire"; "asthma"; "milk"], + "08d7d355bc3391d12d140780b39717d9f46fcf87", + "4092742372031", + "tz1amUjiZaevaxQy5wKn4SSRvVoERCip3nZS", + "9kbZ7fR6im", + "bnyxxzqr.tdszcvqb@tezos.example.org" + ) ; + (["opera"; "divorce"; "easy"; "myself"; "idea"; "aim"; "dash"; "scout"; + "case"; "resource"; "vote"; "humor"; "ticket"; "client"; "edge"], + "9b7cad042fba557618bdc4b62837c5f125b50e56", + "17590039016550", + "tz1Zaee3QBtD4ErY1SzqUvyYTrENrExu6yQM", + "suxT5H09yY", + "iilkhohu.otnyuvna@tezos.example.org" + ) ; + (["token"; "similar"; "ginger"; "tongue"; "gun"; "sort"; "piano"; "month"; + "hotel"; "vote"; "undo"; "success"; "hobby"; "shell"; "cart"], + "124c0ca217f11ffc6c7b76a743d867c8932e5afd", + "26322312350555", + "tz1geDUUhfXK1EMj7VQdRjug1MoFe6gHWnCU", + "4odVdLykaa", + "kwhlglvr.slriitzy@tezos.example.org" + ) ; + (["shield"; "warrior"; "gorilla"; "birth"; "steak"; "neither"; "feel"; + "only"; "liberty"; "float"; "oven"; "extend"; "pulse"; "suffer"; "vapor"], + "ac7a2125beea68caf5266a647f24dce9fea018a7", + "244951387881443", + "tz1h3nY7jcZciJgAwRhWcrEwqfVp7VQoffur", + "A6yeMqBFG8", + "lvrmlbyj.yczltcxn@tezos.example.org" + ) ; + (["waste"; "open"; "scan"; "tip"; "subway"; "dance"; "rent"; "copper"; + "garlic"; "laundry"; "defense"; "clerk"; "another"; "staff"; "liar"], + "2b3e94be133a960fa0ef87f6c0922c19f9d87ca2", + "80065050465525", + "tz1VzL4Xrb3fL3ckvqCWy6bdGMzU2w9eoRqs", + "oVZqpq60sk", + "rfodmrha.zzdndvyk@tezos.example.org" + ) ; + (["fiber"; "next"; "property"; "cradle"; "silk"; "obey"; "gossip"; + "push"; "key"; "second"; "across"; "minimum"; "nice"; "boil"; "age"], + "dac31640199f2babc157aadc0021cd71128ca9ea", + "3569618927693", + "tz1RUHg536oRKhPLFfttcB5gSWAhh4E9TWjX", + "FfytQTTVbu", + "owecikdy.gxnyttya@tezos.example.org" + ) ; + (["print"; "labor"; "budget"; "speak"; "poem"; "diet"; "chunk"; "eternal"; + "book"; "saddle"; "pioneer"; "ankle"; "happy"; "only"; "exclude"], + "bb841227f250a066eb8429e56937ad504d7b34dd", + "9034781424478", + "tz1M1LFbgctcPWxstrao9aLr2ECW1fV4pH5u", + "zknAl3lrX2", + "ettilrvh.zsrqrbud@tezos.example.org" + ) ; + ] + +let activation_init () = + Context.init ~commitments 1 >>=? fun (b, cs) -> + secrets () |> fun ss -> + return (b, cs, ss) + +let simple_init_with_commitments () = + activation_init () >>=? fun (blk, _contracts, _secrets) -> + Block.bake blk >>=? fun _ -> + return () + +(** A single activation *) +let single_activation () = + activation_init () >>=? fun (blk, _contracts, secrets) -> + let { account ; activation_code ; amount=expected_amount ; _ } as _first_one = List.hd secrets in + + (* Contract does not exist *) + Assert.balance_is ~loc:__LOC__ (B blk) (Contract.implicit_contract account) Tez.zero >>=? fun () -> + + Op.activation (B blk) account activation_code >>=? fun operation -> + Block.bake ~operation blk >>=? fun blk -> + + (* Contract does exist *) + Assert.balance_is ~loc:__LOC__ (B blk) (Contract.implicit_contract account) expected_amount + +(** 10 activations, one per bake *) +let multi_activation_1 () = + activation_init () >>=? fun (blk, _contracts, secrets) -> + + Error_monad.fold_left_s (fun blk { account ; activation_code ; amount = expected_amount ; _ } -> + Op.activation (B blk) account activation_code >>=? fun operation -> + Block.bake ~operation blk >>=? fun blk -> + + Assert.balance_is ~loc:__LOC__ (B blk) (Contract.implicit_contract account) expected_amount >>=? fun () -> + + return blk + ) blk secrets >>=? fun _ -> + return () + +(** All in one bake *) +let multi_activation_2 () = + activation_init () >>=? fun (blk, _contracts, secrets) -> + + Error_monad.fold_left_s (fun ops { account ; activation_code ; _ } -> + Op.activation (B blk) account activation_code >>=? fun op -> + return (op::ops) + ) [] secrets >>=? fun ops -> + + Block.bake ~operations:ops blk >>=? fun blk -> + + Error_monad.iter_s (fun { account ; amount = expected_amount ; _ } -> + (* Contract does exist *) + Assert.balance_is ~loc:__LOC__ (B blk) (Contract.implicit_contract account) expected_amount + ) secrets + +(** Transfer with activated account *) +let activation_and_transfer () = + activation_init () >>=? fun (blk, contracts, secrets) -> + let { account ; activation_code ; _ } as _first_one = List.hd secrets in + let bootstrap_contract = List.hd contracts in + let first_contract = Contract.implicit_contract account in + + Op.activation (B blk) account activation_code >>=? fun operation -> + Block.bake ~operation blk >>=? fun blk -> + + Context.Contract.balance (B blk) bootstrap_contract >>=? fun amount -> + Tez.(/?) amount 2L >>?= fun half_amount -> + Context.Contract.balance (B blk) first_contract >>=? fun activated_amount_before -> + + Op.transaction (B blk) bootstrap_contract first_contract half_amount >>=? fun operation -> + Block.bake ~operation blk >>=? fun blk -> + + Assert.balance_was_credited ~loc:__LOC__ (B blk) (Contract.implicit_contract account) activated_amount_before half_amount + +(** Transfer to an unactivated account and then activating it *) +let transfer_to_unactivated_then_activate () = + activation_init () >>=? fun (blk, contracts, secrets) -> + let { account ; activation_code ; amount } as _first_one = List.hd secrets in + let bootstrap_contract = List.hd contracts in + let unactivated_commitment_contract = Contract.implicit_contract account in + + Context.Contract.balance (B blk) bootstrap_contract >>=? fun b_amount -> + Tez.(/?) b_amount 2L >>?= fun b_half_amount -> + + Incremental.begin_construction blk >>=? fun inc -> + Op.transaction (I inc) bootstrap_contract unactivated_commitment_contract b_half_amount >>=? fun op -> + Incremental.add_operation inc op >>=? fun inc -> + Op.activation (I inc) account activation_code >>=? fun op' -> + Incremental.add_operation inc op' >>=? fun inc -> + Incremental.finalize_block inc >>=? fun blk2 -> + + Assert.balance_was_credited ~loc:__LOC__ (B blk2) (Contract.implicit_contract account) amount b_half_amount + +(****************************************************************) +(* The following test scenarios are supposed to raise errors. *) +(****************************************************************) + +(** Invalid pkh activation : expected to fail as the context does not + contain any commitment *) +let invalid_activation_with_no_commitments () = + Context.init 1 >>=? fun (blk, _) -> + let secrets = secrets () in + let { account ; activation_code ; _ } as _first_one = List.hd secrets in + + Op.activation (B blk) account activation_code >>=? fun operation -> + Block.bake ~operation blk >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Invalid_activation _ -> true + | _ -> false + end + +(** Wrong activation : wrong secret given in the operation *) +let invalid_activation_wrong_secret () = + activation_init () >>=? fun (blk, _, secrets) -> + let { account ; _ } as _first_one = List.nth secrets 0 in + let { activation_code ; _ } as _second_one = List.nth secrets 1 in + + Op.activation (B blk) account activation_code >>=? fun operation -> + Block.bake ~operation blk >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Invalid_activation _ -> true + | _ -> false + end + +(** Invalid pkh activation : expected to fail as the context does not + contain an associated commitment *) +let invalid_activation_inexistent_pkh () = + activation_init () >>=? fun (blk, _, secrets) -> + let { activation_code ; _ } as _first_one = List.hd secrets in + let inexistent_pkh = Signature.Public_key_hash.of_b58check_exn + "tz1PeQHGKPWSpNoozvxgqLN9TFsj6rDqNV3o" in + + Op.activation (B blk) inexistent_pkh activation_code >>=? fun operation -> + Block.bake ~operation blk >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Invalid_activation _ -> true + | _ -> false + end + +(** Invalid pkh activation : expected to fail as the commitment has + already been claimed *) +let invalid_double_activation () = + activation_init () >>=? fun (blk, _, secrets) -> + let { account ; activation_code ; _ } as _first_one = List.hd secrets in + Incremental.begin_construction blk >>=? fun inc -> + + Op.activation (I inc) account activation_code >>=? fun op -> + Incremental.add_operation inc op >>=? fun inc -> + Op.activation (I inc) account activation_code >>=? fun op' -> + Incremental.add_operation inc op' >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Invalid_activation _ -> true + | _ -> false + end + +(** Transfer from an unactivated commitment account *) +let invalid_transfer_from_unactived_account () = + activation_init () >>=? fun (blk, contracts, secrets) -> + let { account ; _ } as _first_one = List.hd secrets in + let bootstrap_contract = List.hd contracts in + let unactivated_commitment_contract = Contract.implicit_contract account in + + (* No activation *) + + Op.transaction (B blk) unactivated_commitment_contract bootstrap_contract Tez.one >>=? fun operation -> + Block.bake ~operation blk >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Contract_storage.Empty_implicit_contract pkh -> if pkh = account then true else false + | _ -> false + end + +let tests = [ + Test.tztest "init with commitments" `Quick simple_init_with_commitments ; + Test.tztest "single activation" `Quick single_activation ; + Test.tztest "multi-activation one-by-one" `Quick multi_activation_1 ; + Test.tztest "multi-activation all at a time" `Quick multi_activation_2 ; + Test.tztest "activation and transfer" `Quick activation_and_transfer ; + Test.tztest "transfer to unactivated account then activate" `Quick transfer_to_unactivated_then_activate ; + Test.tztest "invalid activation with no commitments" `Quick invalid_activation_with_no_commitments ; + Test.tztest "invalid activation with commitments" `Quick invalid_activation_inexistent_pkh ; + Test.tztest "invalid double activation" `Quick invalid_double_activation ; + Test.tztest "wrong activation code" `Quick invalid_activation_wrong_secret ; + Test.tztest "invalid transfer from unactivated account" `Quick invalid_transfer_from_unactived_account +] diff --git a/src/proto_alpha/lib_protocol/test/endorsement.ml b/src/proto_alpha/lib_protocol/test/endorsement.ml new file mode 100644 index 000000000..b8aea78e1 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/endorsement.ml @@ -0,0 +1,246 @@ +(**************************************************************************) +(* *) +(* Copyright (c) 2014 - 2018. *) +(* Dynamic Ledger Solutions, Inc. *) +(* *) +(* All rights reserved. No warranty, explicit or implicit, provided. *) +(* *) +(**************************************************************************) + +(** Endorsing a block adds an extra layer of confidence to the Tezos's + PoS algorithm. The block endorsing operation must be included in + the following block. Each endorser possess a slot corresponding to + their priority. After [preserved_cycles], a reward is given to the + endorser. This reward depends on the priority of the endorsed + block. *) + +open Proto_alpha +open Alpha_context +open Test_utils +open Test_tez + +(****************************************************************) +(* Utility functions *) +(****************************************************************) + +let get_expected_reward ?(priority=0) ~nb_baking ~nb_endorsement ctxt = + Context.get_constants ctxt >>=? fun Constants. + { parametric = { endorsement_reward ; block_reward ; _ } ; _ } -> + let open Alpha_environment in let open Tez in + endorsement_reward /? Int64.(succ (of_int priority)) >>?= fun endorsement_reward -> + + endorsement_reward *? (Int64.of_int nb_endorsement) >>?= fun endorsement_reward -> + block_reward *? (Int64.of_int nb_baking) >>?= fun baking_reward -> + endorsement_reward +? baking_reward >>?= fun reward -> return reward + +let get_expected_deposit ctxt ~nb_baking ~nb_endorsement = + Context.get_constants ctxt >>=? fun Constants. + { parametric = { endorsement_security_deposit ; + block_security_deposit ; _ } ; _ } -> + let open Alpha_environment in let open Tez in + endorsement_security_deposit *? (Int64.of_int nb_endorsement) >>?= fun endorsement_deposit -> + block_security_deposit *? (Int64.of_int nb_baking) >>?= fun baking_deposit -> + endorsement_deposit +? baking_deposit >>?= fun deposit -> return deposit + +let assert_endorser_balance_consistency ~loc ?(priority=0) ?(nb_baking=0) ~nb_endorsement + ctxt pkh initial_balance = + let contract = Contract.implicit_contract pkh in + get_expected_reward ~priority ~nb_baking ~nb_endorsement ctxt >>=? fun reward -> + get_expected_deposit ctxt ~nb_baking ~nb_endorsement >>=? fun deposit -> + + Assert.balance_was_debited ~loc ctxt contract initial_balance deposit >>=? fun () -> + Context.Contract.balance ~kind:Rewards ctxt contract >>=? fun reward_balance -> + Assert.equal_tez ~loc reward_balance reward >>=? fun () -> + Context.Contract.balance ~kind:Deposit ctxt contract >>=? fun deposit_balance -> + Assert.equal_tez ~loc deposit_balance deposit + +(****************************************************************) +(* Tests *) +(****************************************************************) + +(** Apply a single endorsement from the slot 0 endorser *) +let simple_endorsement () = + Context.init 5 >>=? fun (b, _) -> + let slot = 0 in + Incremental.begin_construction b >>=? fun inc -> + + Context.get_endorser (B b) slot >>=? fun endorser -> + Op.endorsement ~delegate:endorser (I inc) slot >>=? fun op -> + Incremental.add_operation inc op >>=? fun inc -> + + Context.Contract.balance (B b) (Contract.implicit_contract endorser) >>=? fun initial_balance -> + assert_endorser_balance_consistency ~loc:__LOC__ + (I inc) ~nb_endorsement:1 endorser initial_balance + +(** Apply a maximum number of endorsement. A endorser can be selected + twice. *) +let max_endorsement () = + let endorsers_per_block = 16 in + Context.init ~endorsers_per_block 32 >>=? fun (b, _) -> + + Context.get_endorsers (B b) >>=? fun endorsers -> + Assert.equal_int ~loc:__LOC__ + (List.length endorsers) endorsers_per_block >>=? fun () -> + + fold_left_s (fun (ops, balances) (delegate, slot) -> + Context.Contract.balance (B b) (Contract.implicit_contract delegate) >>=? fun balance -> + Op.endorsement ~delegate (B b) slot >>=? fun op -> + return (op :: ops, balance :: balances) + ) + ([], []) + (List.combine endorsers (0--(endorsers_per_block - 1))) >>=? fun (ops, previous_balances) -> + + Block.bake ~policy:(Excluding endorsers) ~operations:(List.rev ops) b >>=? fun b -> + + let count acc = + List.find_all ((=) acc) endorsers |> List.length + in + + (* One account can endorse more than one time per level, we must + check that the bonds are summed up *) + iter_s (fun (endorser_account, previous_balance) -> + let nb_endorsement = count endorser_account in + assert_endorser_balance_consistency ~loc:__LOC__ + (B b) ~nb_endorsement endorser_account previous_balance + ) (List.combine endorsers (List.rev previous_balances)) + +(** Check that an endorser balance is consistent with a different piority *) +let consistent_priority () = + Context.init 32 >>=? fun (b, _) -> + Block.get_next_baker ~policy:(By_priority 15) b >>=? fun (baker_account, _, _) -> + Block.bake ~policy:(By_priority 15) b >>=? fun b -> + + (* Grab an endorser that didn't bake the previous block *) + Context.get_endorser (B b) 0 >>=? fun endorser_0 -> + Context.get_endorser (B b) 1 >>=? fun endorser_1 -> + let (endorser, slot) = + if endorser_0 = baker_account then endorser_1, 1 else endorser_0, 0 + in + Context.Contract.balance (B b) (Contract.implicit_contract endorser) >>=? fun balance -> + + Op.endorsement ~delegate:endorser (B b) slot >>=? fun operation -> + Block.bake ~policy:( Excluding [ endorser ] ) ~operation b >>=? fun b -> + + assert_endorser_balance_consistency ~loc:__LOC__ ~priority:15 + (B b) ~nb_endorsement:1 endorser balance + +(** Check every 32 endorser's balances are consistent with a different piority *) +let consistent_priorities () = + let priorities = 15 -- 31 in + Context.init 64 >>=? fun (b, _) -> + + iter_s (fun priority -> + (* Bake with a specific priority *) + Block.get_next_baker ~policy:(By_priority priority) b >>=? fun (baker_account, _, _) -> + Block.bake ~policy:(By_priority priority) b >>=? fun b -> + + (* Grab an endorser that didn't bake the previous block *) + Context.get_endorser (B b) 0 >>=? fun endorser_0 -> + Context.get_endorser (B b) 1 >>=? fun endorser_1 -> + let (endorser, slot) = + if endorser_0 = baker_account then endorser_1, 1 else endorser_0, 0 + in + + Context.Contract.balance (B b) (Contract.implicit_contract endorser) >>=? fun balance -> + Op.endorsement ~delegate:endorser (B b) slot >>=? fun operation -> + Block.bake ~policy:( Excluding [ endorser ] ) ~operation b >>=? fun b -> + + assert_endorser_balance_consistency ~loc:__LOC__ ~priority + (B b) ~nb_endorsement:1 endorser balance + ) priorities + +(** Check that after a cycle the endorser gets his reward *) +let reward_retrieval () = + Context.init 5 >>=? fun (b, _) -> + Context.get_constants (B b) >>=? fun Constants. + { parametric = { preserved_cycles ; endorsement_reward ; _ } ; _ } -> + + let slot = 0 in + Context.get_endorser (B b) slot >>=? fun endorser -> + Context.Contract.balance (B b) (Contract.implicit_contract endorser) >>=? fun balance -> + Op.endorsement ~delegate:endorser (B b) slot >>=? fun operation -> + Block.bake ~policy:(Excluding [ endorser ]) ~operation b >>=? fun b -> + (* Bake (preserved_cycles + 1) cycles *) + fold_left_s (fun b _ -> + Block.bake_until_cycle_end ~policy:(Excluding [ endorser ]) b + ) b (0 -- preserved_cycles) >>=? fun b -> + + Assert.balance_was_credited ~loc:__LOC__ (B b) (Contract.implicit_contract endorser) balance endorsement_reward + +(****************************************************************) +(* The following test scenarios are supposed to raise errors. *) +(****************************************************************) + +(** Wrong endorsement predecessor : apply an endorsement with an + incorrect block predecessor *) +let wrong_endorsement_predecessor () = + Context.init 5 >>=? fun (b, _) -> + + Context.get_endorser (B b) 0 >>=? fun genesis_endorser -> + Block.bake b >>=? fun b' -> + Op.endorsement ~delegate:genesis_endorser ~signing_context:(B b') (B b) 0 >>=? fun operation -> + Block.bake ~operation b' >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Wrong_endorsement_predecessor _ -> true + | _ -> false + end + +(** Invalid_endorsement_level : apply an endorsement with an incorrect + level (i.e. the predecessor level) *) +let invalid_endorsement_level () = + Context.init 5 >>=? fun (b, _) -> + Context.get_level (B b) >>=? fun genesis_level -> + Block.bake b >>=? fun b -> + Op.endorsement ~level:genesis_level (B b) 0 >>=? fun operation -> + Block.bake ~operation b >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Invalid_endorsement_level -> true + | _ -> false + end + +(** Duplicate endorsement : apply an endorsement that has already been done *) +let duplicate_endorsement () = + Context.init 5 >>=? fun (b, _) -> + Incremental.begin_construction b >>=? fun inc -> + Op.endorsement (B b) 0 >>=? fun operation -> + Incremental.add_operation inc operation >>=? fun inc -> + Op.endorsement (B b) 0 >>=? fun operation -> + Incremental.add_operation inc operation >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Apply.Duplicate_endorsement _ -> true + | _ -> false + end + +(** Invalid_endorsement_slot : making an endorsement with an invalid slot *) +let invalid_endorsement_slot () = + Context.init 64 >>=? fun (b, _) -> + Context.get_constants (B b) >>=? fun Constants. + { parametric = { endorsers_per_block ; _ } ; _ } -> + + Op.endorsement (B b) (endorsers_per_block + 1) >>=? fun operation -> + + Block.bake ~operation b >>= fun res -> + + Assert.proto_error ~loc:__LOC__ res begin function + | Baking.Invalid_endorsement_slot _ -> true + | _ -> false + end + +let tests = [ + Test.tztest "Simple endorsement" `Quick simple_endorsement ; + Test.tztest "Maximum endorsement" `Quick max_endorsement ; + + Test.tztest "Consistent priority" `Quick consistent_priority ; + Test.tztest "Consistent priorities" `Quick consistent_priorities ; + Test.tztest "Reward retrieval" `Quick reward_retrieval ; + + (* Fail scenarios *) + Test.tztest "Wrong endorsement predecessor" `Quick wrong_endorsement_predecessor ; + Test.tztest "Invalid endorsement level" `Quick invalid_endorsement_level ; + Test.tztest "Duplicate endorsement" `Quick duplicate_endorsement ; + + Test.tztest "Invalid endorsement slot" `Quick invalid_endorsement_slot ; +] diff --git a/src/proto_alpha/lib_protocol/test/main.ml b/src/proto_alpha/lib_protocol/test/main.ml index 5e4c4a210..0d09da75a 100644 --- a/src/proto_alpha/lib_protocol/test/main.ml +++ b/src/proto_alpha/lib_protocol/test/main.ml @@ -11,4 +11,6 @@ let () = Alcotest.run "protocol_alpha" [ "transfer", Transfer.tests ; "origination", Origination.tests ; + "activation", Activation.tests ; + "endorsement", Endorsement.tests ; ]