251 lines
12 KiB
OCaml
251 lines
12 KiB
OCaml
|
(*****************************************************************************)
|
||
|
(* *)
|
||
|
(* Open Source License *)
|
||
|
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||
|
(* *)
|
||
|
(* Permission is hereby granted, free of charge, to any person obtaining a *)
|
||
|
(* copy of this software and associated documentation files (the "Software"),*)
|
||
|
(* to deal in the Software without restriction, including without limitation *)
|
||
|
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
|
||
|
(* and/or sell copies of the Software, and to permit persons to whom the *)
|
||
|
(* Software is furnished to do so, subject to the following conditions: *)
|
||
|
(* *)
|
||
|
(* The above copyright notice and this permission notice shall be included *)
|
||
|
(* in all copies or substantial portions of the Software. *)
|
||
|
(* *)
|
||
|
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
|
||
|
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
|
||
|
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
|
||
|
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
|
||
|
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
|
||
|
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
|
||
|
(* DEALINGS IN THE SOFTWARE. *)
|
||
|
(* *)
|
||
|
(*****************************************************************************)
|
||
|
|
||
|
open Protocol
|
||
|
open Alpha_context
|
||
|
open Test_tez
|
||
|
open Test_utils
|
||
|
|
||
|
let account_pair = function
|
||
|
| [a1; a2] -> (a1, a2)
|
||
|
| _ -> assert false
|
||
|
|
||
|
let wrap e = Lwt.return (Environment.wrap_error e)
|
||
|
let traverse_rolls ctxt head =
|
||
|
let rec loop acc roll =
|
||
|
Storage.Roll.Successor.get_option ctxt roll >>= wrap >>=? function
|
||
|
| None -> return (List.rev acc)
|
||
|
| Some next -> loop (next :: acc) next in
|
||
|
loop [head] head
|
||
|
|
||
|
let get_rolls ctxt delegate =
|
||
|
Storage.Roll.Delegate_roll_list.get_option ctxt delegate >>= wrap >>=? function
|
||
|
| None -> return_nil
|
||
|
| Some head_roll -> traverse_rolls ctxt head_roll
|
||
|
|
||
|
let check_rolls b (account:Account.t) =
|
||
|
Context.get_constants (B b) >>=? fun constants ->
|
||
|
Context.Delegate.info (B b) account.pkh >>=? fun { staking_balance ; _ } ->
|
||
|
let token_per_roll = constants.parametric.tokens_per_roll in
|
||
|
let expected_rolls = Int64.div (Tez.to_mutez staking_balance) (Tez.to_mutez token_per_roll) in
|
||
|
Raw_context.prepare b.context
|
||
|
~level:b.header.shell.level
|
||
|
~predecessor_timestamp:b.header.shell.timestamp
|
||
|
~timestamp:b.header.shell.timestamp
|
||
|
~fitness:b.header.shell.fitness >>= wrap >>=? fun ctxt ->
|
||
|
get_rolls ctxt account.pkh >>=? fun rolls ->
|
||
|
Assert.equal_int ~loc:__LOC__ (List.length rolls) (Int64.to_int expected_rolls)
|
||
|
|
||
|
let check_no_rolls (b : Block.t) (account:Account.t) =
|
||
|
Raw_context.prepare b.context
|
||
|
~level:b.header.shell.level
|
||
|
~predecessor_timestamp:b.header.shell.timestamp
|
||
|
~timestamp:b.header.shell.timestamp
|
||
|
~fitness:b.header.shell.fitness >>= wrap >>=? fun ctxt ->
|
||
|
get_rolls ctxt account.pkh >>=? fun rolls ->
|
||
|
Assert.equal_int ~loc:__LOC__ (List.length rolls) 0
|
||
|
|
||
|
let simple_staking_rights () =
|
||
|
Context.init 2 >>=? fun (b,accounts) ->
|
||
|
let (a1, _a2) = account_pair accounts in
|
||
|
|
||
|
Context.Contract.balance (B b) a1 >>=? fun balance ->
|
||
|
Context.Contract.manager (B b) a1 >>=? fun m1 ->
|
||
|
|
||
|
Context.Delegate.info (B b) m1.pkh >>=? fun info ->
|
||
|
Assert.equal_tez ~loc:__LOC__ balance info.staking_balance >>=? fun () ->
|
||
|
check_rolls b m1
|
||
|
|
||
|
let simple_staking_rights_after_baking () =
|
||
|
Context.init 2 >>=? fun (b,accounts) ->
|
||
|
let (a1, a2) = account_pair accounts in
|
||
|
|
||
|
Context.Contract.balance (B b) a1 >>=? fun balance ->
|
||
|
Context.Contract.manager (B b) a1 >>=? fun m1 ->
|
||
|
Context.Contract.manager (B b) a2 >>=? fun m2 ->
|
||
|
|
||
|
Block.bake_n ~policy:(By_account m2.pkh) 5 b >>=? fun b ->
|
||
|
|
||
|
Context.Delegate.info (B b) m1.pkh >>=? fun info ->
|
||
|
Assert.equal_tez ~loc:__LOC__ balance info.staking_balance >>=? fun () ->
|
||
|
check_rolls b m1 >>=? fun () ->
|
||
|
check_rolls b m2
|
||
|
|
||
|
let frozen_deposit (info:Context.Delegate.info) =
|
||
|
Cycle.Map.fold (fun _ { Delegate.deposit ; _ } acc ->
|
||
|
Test_tez.Tez.(deposit + acc))
|
||
|
info.frozen_balance_by_cycle Tez.zero
|
||
|
|
||
|
let check_activate_staking_balance ~loc ~deactivated b (a, (m:Account.t)) =
|
||
|
Context.Delegate.info (B b) m.pkh >>=? fun info ->
|
||
|
Assert.equal_bool ~loc info.deactivated deactivated >>=? fun () ->
|
||
|
Context.Contract.balance (B b) a >>=? fun balance ->
|
||
|
let deposit = frozen_deposit info in
|
||
|
Assert.equal_tez ~loc Test_tez.Tez.(balance + deposit) info.staking_balance
|
||
|
|
||
|
let run_until_deactivation () =
|
||
|
Context.init 2 >>=? fun (b,accounts) ->
|
||
|
let (a1, a2) = account_pair accounts in
|
||
|
|
||
|
Context.Contract.balance (B b) a1 >>=? fun balance_start ->
|
||
|
Context.Contract.manager (B b) a1 >>=? fun m1 ->
|
||
|
Context.Contract.manager (B b) a2 >>=? fun m2 ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b (a1,m1) >>=? fun () ->
|
||
|
|
||
|
Context.Delegate.info (B b) m1.pkh >>=? fun info ->
|
||
|
Block.bake_until_cycle ~policy:(By_account m2.pkh) info.grace_period b >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b (a1,m1) >>=? fun () ->
|
||
|
|
||
|
Block.bake_until_cycle_end ~policy:(By_account m2.pkh) b >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:true b (a1,m1) >>=? fun () ->
|
||
|
return (b, ((a1, m1), balance_start), (a2, m2))
|
||
|
|
||
|
let deactivation_then_bake () =
|
||
|
run_until_deactivation () >>=?
|
||
|
fun (b, ((_deactivated_contract, deactivated_account) as deactivated, _start_balance),
|
||
|
(_a2, _m2)) ->
|
||
|
|
||
|
Block.bake ~policy:(By_account deactivated_account.pkh) b >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b deactivated >>=? fun () ->
|
||
|
check_rolls b deactivated_account
|
||
|
|
||
|
let deactivation_then_self_delegation () =
|
||
|
run_until_deactivation () >>=?
|
||
|
fun (b, ((deactivated_contract, deactivated_account) as deactivated, start_balance),
|
||
|
(_a2, m2)) ->
|
||
|
|
||
|
Op.delegation (B b) deactivated_contract (Some deactivated_account.pkh) >>=? fun self_delegation ->
|
||
|
|
||
|
Block.bake ~policy:(By_account m2.pkh) b ~operation:self_delegation >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b deactivated >>=? fun () ->
|
||
|
Context.Contract.balance (B b) deactivated_contract >>=? fun balance ->
|
||
|
Assert.equal_tez ~loc:__LOC__ start_balance balance >>=? fun () ->
|
||
|
check_rolls b deactivated_account
|
||
|
|
||
|
let deactivation_then_empty_then_self_delegation () =
|
||
|
run_until_deactivation () >>=?
|
||
|
fun (b, ((deactivated_contract, deactivated_account) as deactivated, _start_balance),
|
||
|
(_a2, m2)) ->
|
||
|
(* empty the contract *)
|
||
|
Context.Contract.balance (B b) deactivated_contract >>=? fun balance ->
|
||
|
let sink_account = Account.new_account () in
|
||
|
let sink_contract = Contract.implicit_contract sink_account.pkh in
|
||
|
Context.get_constants (B b) >>=? fun { parametric = { origination_size ; cost_per_byte ; _ } ; _ } ->
|
||
|
Tez.(cost_per_byte *? Int64.of_int origination_size) >>?= fun origination_burn ->
|
||
|
let amount = match Tez.(balance -? origination_burn) with Ok r -> r | Error _ -> assert false in
|
||
|
Op.transaction (B b) deactivated_contract sink_contract amount >>=? fun empty_contract ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) ~operation:empty_contract b >>=? fun b ->
|
||
|
(* self delegation *)
|
||
|
Op.delegation (B b) deactivated_contract (Some deactivated_account.pkh) >>=? fun self_delegation ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) ~operation:self_delegation b >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b deactivated >>=? fun () ->
|
||
|
Context.Contract.balance (B b) deactivated_contract >>=? fun balance ->
|
||
|
Assert.equal_tez ~loc:__LOC__ Tez.zero balance >>=? fun () ->
|
||
|
check_rolls b deactivated_account
|
||
|
|
||
|
let deactivation_then_empty_then_self_delegation_then_recredit () =
|
||
|
run_until_deactivation () >>=?
|
||
|
fun (b, ((deactivated_contract, deactivated_account) as deactivated, balance),
|
||
|
(_a2, m2)) ->
|
||
|
(* empty the contract *)
|
||
|
let sink_account = Account.new_account () in
|
||
|
let sink_contract = Contract.implicit_contract sink_account.pkh in
|
||
|
Context.get_constants (B b) >>=? fun { parametric = { origination_size ; cost_per_byte ; _ } ; _ } ->
|
||
|
Tez.(cost_per_byte *? Int64.of_int origination_size) >>?= fun origination_burn ->
|
||
|
let amount = match Tez.(balance -? origination_burn) with Ok r -> r | Error _ -> assert false in
|
||
|
Op.transaction (B b) deactivated_contract sink_contract amount >>=? fun empty_contract ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) ~operation:empty_contract b >>=? fun b ->
|
||
|
(* self delegation *)
|
||
|
Op.delegation (B b) deactivated_contract (Some deactivated_account.pkh) >>=? fun self_delegation ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) ~operation:self_delegation b >>=? fun b ->
|
||
|
(* recredit *)
|
||
|
Op.transaction (B b) sink_contract deactivated_contract amount >>=? fun recredit_contract ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) ~operation:recredit_contract b >>=? fun b ->
|
||
|
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b deactivated >>=? fun () ->
|
||
|
Context.Contract.balance (B b) deactivated_contract >>=? fun balance ->
|
||
|
Assert.equal_tez ~loc:__LOC__ amount balance >>=? fun () ->
|
||
|
check_rolls b deactivated_account
|
||
|
|
||
|
let delegation () =
|
||
|
Context.init 2 >>=? fun (b,accounts) ->
|
||
|
let (a1, a2) = account_pair accounts in
|
||
|
let m3 = Account.new_account () in
|
||
|
Account.add_account m3;
|
||
|
|
||
|
Context.Contract.manager (B b) a1 >>=? fun m1 ->
|
||
|
Context.Contract.manager (B b) a2 >>=? fun m2 ->
|
||
|
let a3 = Contract.implicit_contract m3.pkh in
|
||
|
|
||
|
Context.Contract.delegate_opt (B b) a1 >>=? fun delegate ->
|
||
|
begin
|
||
|
match delegate with
|
||
|
| None -> assert false
|
||
|
| Some pkh ->
|
||
|
assert (Signature.Public_key_hash.equal pkh m1.pkh)
|
||
|
end;
|
||
|
|
||
|
Op.transaction (B b) a1 a3 Tez.fifty_cents >>=? fun transact ->
|
||
|
|
||
|
Block.bake ~policy:(By_account m2.pkh) b ~operation:transact >>=? fun b ->
|
||
|
|
||
|
Context.Contract.delegate_opt (B b) a3 >>=? fun delegate ->
|
||
|
begin
|
||
|
match delegate with
|
||
|
| None -> ()
|
||
|
| Some _ -> assert false
|
||
|
end;
|
||
|
check_no_rolls b m3 >>=? fun () ->
|
||
|
|
||
|
Op.delegation (B b) a3 (Some m3.pkh) >>=? fun delegation ->
|
||
|
Block.bake ~policy:(By_account m2.pkh) b ~operation:delegation >>=? fun b ->
|
||
|
|
||
|
Context.Contract.delegate_opt (B b) a3 >>=? fun delegate ->
|
||
|
begin
|
||
|
match delegate with
|
||
|
| None -> assert false
|
||
|
| Some pkh ->
|
||
|
assert (Signature.Public_key_hash.equal pkh m3.pkh)
|
||
|
end;
|
||
|
check_activate_staking_balance ~loc:__LOC__ ~deactivated:false b (a3,m3) >>=? fun () ->
|
||
|
check_rolls b m3 >>=? fun () ->
|
||
|
check_rolls b m1
|
||
|
|
||
|
let tests = [
|
||
|
Test.tztest "simple staking rights" `Quick (simple_staking_rights) ;
|
||
|
Test.tztest "simple staking rights after baking" `Quick (simple_staking_rights_after_baking) ;
|
||
|
Test.tztest "deactivation then bake" `Quick (deactivation_then_bake) ;
|
||
|
Test.tztest "deactivation then self delegation" `Quick (deactivation_then_self_delegation) ;
|
||
|
Test.tztest "deactivation then empty then self delegation" `Quick (deactivation_then_empty_then_self_delegation) ;
|
||
|
Test.tztest "deactivation then empty then self delegation then recredit" `Quick (deactivation_then_empty_then_self_delegation_then_recredit) ;
|
||
|
Test.tztest "delegation" `Quick (delegation) ;
|
||
|
]
|