diff --git a/deku-p/src/core/ledger/ledger.ml b/deku-p/src/core/ledger/ledger.ml index 87fba210ca..72603e6ded 100644 --- a/deku-p/src/core/ledger/ledger.ml +++ b/deku-p/src/core/ledger/ledger.ml @@ -3,10 +3,12 @@ open Deku_concepts open Deku_stdlib exception Insufficient_funds +exception Withdraw_zero_ticket let () = Printexc.register_printer (function | Insufficient_funds -> Some "Ledger error: insufficient funds" + | Withdraw_zero_ticket -> Some "Ledger error: cannot withdraw 0 tickets" | _ -> None) module Withdrawal_handle = struct @@ -116,7 +118,10 @@ let withdraw ~sender ~destination ~amount ~ticket_id t = in let%ok table = Ticket_table.withdraw ~sender ~amount ~ticket_id table - |> Result.map_error (function _ -> Insufficient_funds) + |> Result.map_error (function error -> + (match error with + | `Withdraw_zero_ticket -> Withdraw_zero_ticket + | `Insufficient_funds -> Insufficient_funds)) in let withdrawal_handles, handle = Withdrawal_handle_tree.add diff --git a/deku-p/src/core/ledger/ledger.mli b/deku-p/src/core/ledger/ledger.mli index cc45c0b6a2..7cd7749b58 100644 --- a/deku-p/src/core/ledger/ledger.mli +++ b/deku-p/src/core/ledger/ledger.mli @@ -2,6 +2,7 @@ open Deku_crypto open Deku_concepts exception Insufficient_funds +exception Withdraw_zero_ticket module Withdrawal_handle : sig module Withdrawal_handle_hash : sig diff --git a/deku-p/src/core/ledger/tests/deku_ledger_tests.ml b/deku-p/src/core/ledger/tests/deku_ledger_tests.ml new file mode 100644 index 0000000000..1485cd3ab6 --- /dev/null +++ b/deku-p/src/core/ledger/tests/deku_ledger_tests.ml @@ -0,0 +1 @@ +let () = Test_address.run () diff --git a/deku-p/src/core/ledger/tests/deku_ledger_tests.mli b/deku-p/src/core/ledger/tests/deku_ledger_tests.mli new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deku-p/src/core/ledger/tests/dune b/deku-p/src/core/ledger/tests/dune new file mode 100644 index 0000000000..0256ce85e3 --- /dev/null +++ b/deku-p/src/core/ledger/tests/dune @@ -0,0 +1,10 @@ +(executable + (name deku_ledger_tests) + (libraries deku_ledger alcotest)) + +(rule + (alias runtest) + (deps + (:exe ./deku_ledger_tests.exe)) + (action + (run %{exe}))) diff --git a/deku-p/src/core/ledger/tests/test_address.ml b/deku-p/src/core/ledger/tests/test_address.ml new file mode 100644 index 0000000000..616d1c84cb --- /dev/null +++ b/deku-p/src/core/ledger/tests/test_address.ml @@ -0,0 +1,84 @@ +open Deku_ledger + +let addr = Alcotest.testable Address.pp Address.equal + +let test_address_from_string () = + let address = "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" in + let address = Address.of_b58 address in + let is_some = Option.is_some address in + Alcotest.(check bool) "Address has been parsed" true is_some + +let test_wrong_address_from_string () = + let address = "ksGHeh4SfkGAnzT6UKMDoS4hZjNmoEKhGsK" in + let address = Address.of_b58 address in + let is_none = Option.is_none address in + Alcotest.(check bool) "Address cannot be parsed" true is_none + +let test_b58 () = + let address_string = "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" in + let address_b58 = + address_string |> Address.of_b58 |> Option.get |> Address.to_b58 + in + Alcotest.(check string) + "of_b58 |> to_b58 should returns the input" address_string address_b58 + +let test_key_hash () = + let open Deku_crypto in + let raw_key_hash = + Key_hash.of_b58 "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" |> Option.get + in + let key_hash = + raw_key_hash |> Address.of_key_hash |> Address.to_key_hash |> Option.get + in + Alcotest.(check bool) + "of_key_hash |> to_key_hash shouyld returns the input" true + (Key_hash.equal raw_key_hash key_hash) + +let test_yojson () = + let raw_address = + Address.of_b58 "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" |> Option.get + in + let address_yojson = + raw_address |> Address.yojson_of_t |> Address.t_of_yojson + in + Alcotest.(check addr) + "yojson_of |> of_yojson should returns the input" raw_address address_yojson + +let test_address_equal () = + let address = + Address.of_b58 "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" |> Option.get + in + Alcotest.(check bool) + "two same addresses must be equal" true + (Address.equal address address) + +let test_address_not_equal () = + let address1 = + Address.of_b58 "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" |> Option.get + in + let address2 = + Address.of_b58 "tz1PYdVbnLwiqKo3fLFXTKxw6K7BhpddQPh8" |> Option.get + in + Alcotest.(check bool) + "two different addresses must not be equal" false + (Address.equal address1 address2) + +let run () = + let open Alcotest in + run "Address" ~and_exit:false + [ + ( "Repr", + [ + test_case "parse a correct string address " `Quick + test_address_from_string; + test_case "parsed an incorrect string address" `Quick + test_wrong_address_from_string; + test_case "of_b58 is inverse of from_b58" `Quick test_b58; + test_case "of_key_hash is the inverse of to_key_hash" `Quick + test_key_hash; + test_case "yojson_of is the inverse of of_yojson" `Quick test_yojson; + test_case "two same addresses are equal" `Quick test_address_equal; + test_case "two different addresses are not equal" `Quick + test_address_not_equal; + ] ); + ] diff --git a/deku-p/src/core/ledger/tests/test_address.mli b/deku-p/src/core/ledger/tests/test_address.mli new file mode 100644 index 0000000000..733b2a3231 --- /dev/null +++ b/deku-p/src/core/ledger/tests/test_address.mli @@ -0,0 +1 @@ +val run : unit -> unit diff --git a/deku-p/src/core/ledger/ticket_table.ml b/deku-p/src/core/ledger/ticket_table.ml index 6ffbb6a9ca..14a7e22089 100644 --- a/deku-p/src/core/ledger/ticket_table.ml +++ b/deku-p/src/core/ledger/ticket_table.ml @@ -94,6 +94,10 @@ let transfer ~sender ~receiver ~ticket_id ~amount t = Ok t let withdraw ~sender ~ticket_id ~amount t = + let%ok () = + if Amount.equal Amount.zero amount then Error `Withdraw_zero_ticket + else Ok () + in let%ok map = Address_map.find_opt sender t |> Option.to_result ~none:`Insufficient_funds in diff --git a/deku-p/src/core/ledger/ticket_table.mli b/deku-p/src/core/ledger/ticket_table.mli index 8d27758146..e4770179b3 100644 --- a/deku-p/src/core/ledger/ticket_table.mli +++ b/deku-p/src/core/ledger/ticket_table.mli @@ -24,7 +24,7 @@ val withdraw : ticket_id:Ticket_id.t -> amount:Amount.t -> t -> - (t, [> `Insufficient_funds ]) result + (t, [> `Insufficient_funds | `Withdraw_zero_ticket ]) result val take_tickets : sender:Address.t -> diff --git a/deku-p/src/core/protocol/tests/deku_protocol_tests.ml b/deku-p/src/core/protocol/tests/deku_protocol_tests.ml index d8ef958fbf..aed249214c 100644 --- a/deku-p/src/core/protocol/tests/deku_protocol_tests.ml +++ b/deku-p/src/core/protocol/tests/deku_protocol_tests.ml @@ -1,4 +1,5 @@ let () = Test_ledger.run (); Test_operation.run (); - Test_protocol.run () + Test_protocol.run (); + Test_operation_hash.run () diff --git a/deku-p/src/core/protocol/tests/test_operation_hash.ml b/deku-p/src/core/protocol/tests/test_operation_hash.ml new file mode 100644 index 0000000000..f355974aa4 --- /dev/null +++ b/deku-p/src/core/protocol/tests/test_operation_hash.ml @@ -0,0 +1,62 @@ +open Deku_protocol + +let correct_operation_hash = + "Do2YxhRwd1bK5H9A5ULe6eS2bxsMsA8hgVbCxvbpSLQcyW9bkV5S" + +let incorrect_operation_hash = "tz1fpf9DffkGAnzT6UKMDoS4hZjNmoEKhGsK" + +let test_operation_hash_from_correct_string () = + let operation_hash = Operation_hash.of_b58 correct_operation_hash in + let is_some = Option.is_some operation_hash in + Alcotest.(check bool) "Operation has been parsed" true is_some + +let test_operation_hash_from_incorrect_string () = + let operation_hash = Operation_hash.of_b58 incorrect_operation_hash in + let is_none = Option.is_none operation_hash in + Alcotest.(check bool) "Operation has not been parsed" true is_none + +let test_b58 () = + let result = + correct_operation_hash |> Operation_hash.of_b58 |> Option.get + |> Operation_hash.to_b58 + in + Alcotest.(check string) "b58 should be the same" correct_operation_hash result + +let test_prefix () = + let operation_hash = Operation_hash.hash "hello" in + let operation = Operation_hash.to_b58 operation_hash in + Alcotest.(check bool) + "b58 should start by Do" true + (String.starts_with ~prefix:"Do" operation) + +let test_equal () = + let operation_1 = Operation_hash.hash "hello" in + let operation_2 = Operation_hash.hash "hello" in + Alcotest.(check bool) + "should be equal" true + (Operation_hash.equal operation_1 operation_2) + +let test_not_equal () = + let operation_1 = Operation_hash.hash "hello" in + let operation_2 = Operation_hash.hash "world" in + Alcotest.(check bool) + "should be equal" false + (Operation_hash.equal operation_1 operation_2) + +let run () = + let open Alcotest in + run "Operation hash" ~and_exit:false + [ + ( "Repr", + [ + test_case "parse a correct operation address" `Quick + test_operation_hash_from_correct_string; + test_case "parse an incorrect operation address" `Quick + test_operation_hash_from_incorrect_string; + test_case "of_b58 is inverse of to_b58" `Quick test_b58; + test_case "Do is the prefix of an operation hash" `Quick test_prefix; + test_case "Two same operation hash should be equal" `Quick test_equal; + test_case "Two different operation hash should not be equal" `Quick + test_not_equal; + ] ); + ] diff --git a/deku-p/src/core/protocol/tests/test_operation_hash.mli b/deku-p/src/core/protocol/tests/test_operation_hash.mli new file mode 100644 index 0000000000..733b2a3231 --- /dev/null +++ b/deku-p/src/core/protocol/tests/test_operation_hash.mli @@ -0,0 +1 @@ +val run : unit -> unit diff --git a/deku-p/src/core/protocol/tests/test_protocol.ml b/deku-p/src/core/protocol/tests/test_protocol.ml index bce5b32e95..0d364b01d8 100644 --- a/deku-p/src/core/protocol/tests/test_protocol.ml +++ b/deku-p/src/core/protocol/tests/test_protocol.ml @@ -10,12 +10,17 @@ let alice_secret = |> Option.get let alice = Identity.make alice_secret +let alice_addr = Address.of_key_hash (Identity.key_hash alice) let bob_secret = Secret.of_b58 "edsk4Qejwxwj7JD93B45gvhYHVfMzNjkBWRQDaYkdt5JcUWLT4VDkh" |> Option.get let bob = Identity.make bob_secret +let bob_addr = Address.of_key_hash (Identity.key_hash bob) + +let bob_tezos = + bob_addr |> Address.to_b58 |> Deku_tezos.Address.of_string |> Option.get let ticket_id = let address = @@ -25,6 +30,8 @@ let ticket_id = let data = Bytes.of_string "" in Ticket_id.make (Tezos address) data +let amount' = Alcotest.testable Amount.pp Amount.equal + (* helper to create in an easy way an operation that transfer n token from alice to bob *) let make_operation ?(nonce = 1) ?(level = 0) ?(amount = 0) () = let level = Level.of_n (N.of_z (Z.of_int level) |> Option.get) in @@ -341,6 +348,368 @@ let test_cannot_create_amount_ex_nihilo () = Alcotest.(check amount) "balance of bob has not changed" alice_previous_balance alice_balance +let test_noop_operation_no_exceptions () = + let nonce = Nonce.of_n N.one in + let level = Level.of_n N.one in + let noop = Operation.Signed.noop ~identity:bob ~nonce ~level in + let (Operation.Signed.Signed_operation { initial; _ }) = noop in + let payload = [ initial ] in + + let _, _, exn = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] + Protocol.initial + in + Alcotest.(check int) + "noop operation should not produce exceptions" 0 (List.length exn) + +(* + TODO: I think it can be a source a bug + What if a user submit a noop operations ? + - The operation is added to the mempool + - And will never be removed ? + - If the memory pool is full of noop operations, is it still possible to submit operations +*) +let test_noop_operation_empty_receipts () = + let nonce = Nonce.of_n N.one in + let level = Level.of_n N.one in + let noop = Operation.Signed.noop ~identity:bob ~nonce ~level in + let (Operation.Signed.Signed_operation { initial; _ }) = noop in + let payload = [ initial ] in + + let _, receipts, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] + Protocol.initial + in + Alcotest.(check int) + "noop operation should not produce exceptions" 0 (List.length receipts) + +let test_noop_operation_does_nothing () = + let nonce = Nonce.of_n N.one in + let level = Level.of_n N.one in + let noop = Operation.Signed.noop ~identity:bob ~nonce ~level in + let (Operation.Signed.Signed_operation { initial; _ }) = noop in + let payload = [ initial ] in + let (Protocol.Protocol + { ledger = initial_ledger; vm_state = initial_vm_state; _ }) = + Protocol.initial + in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] + Protocol.initial + in + let (Protocol.Protocol { ledger = next_ledger; vm_state = next_vm_state; _ }) + = + protocol + in + Alcotest.(check bool) + "noop operation should not change the ledger" true + (initial_ledger = next_ledger); + Alcotest.(check bool) + "noop operation should not change the vm state" true + (initial_vm_state = next_vm_state) + +(* Maybe those tests are redundant with Ledger related test *) +let ticket_id = + let ticketer = + Ticket_id.Tezos + (Deku_tezos.Contract_hash.of_b58 "KT1Us9LZaG8F6cskmMg1hB2FPRwakWkegkPi" + |> Option.get) + in + let data = Bytes.of_string "hello" in + Ticket_id.make ticketer data + +let transfer ~level ~sender ~receiver ~amount = + let nonce = Nonce.of_n N.one in + let transfer = + Operation.Signed.ticket_transfer ~identity:sender ~nonce ~level ~receiver + ~ticket_id ~amount + in + let (Operation.Signed.Signed_operation { initial; _ }) = transfer in + initial + +let test_ticket_transfer_undefined_ticket_exn () = + let amount = Amount.of_n N.zero in + let level = Level.of_n N.one in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let protocol = Protocol.initial in + let _, _, exns = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + Alcotest.(check int) + "transfering undefined token should returns exception" 1 (List.length exns) + +let test_ticket_transfer_undefined_ticket () = + let amount = Amount.of_n N.zero in + let level = Level.of_n N.one in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let protocol = Protocol.initial in + let (Protocol.Protocol { ledger; _ }) = protocol in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger = next_ledger; _ }) = protocol in + Alcotest.(check bool) + "transfering undefined token should not affect the ledger" true + (ledger = next_ledger) + +let test_ticket_transfer_undefined_ticket_receipt () = + let amount = Amount.of_n N.zero in + let level = Level.of_n N.one in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let protocol = Protocol.initial in + let _, receipts, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + Alcotest.(check int) + "transfering undefined token should return a receipt" 1 + (List.length receipts) + +let protocol_with_ledger ~ledger = + let ledger = Ledger.yojson_of_t ledger in + let assoc = + match Protocol.yojson_of_t Protocol.initial with + | `Variant (_, Some assoc) -> assoc + | `List [ _; assoc ] -> assoc + | _ -> failwith "yojson error" + in + let assoc = + assoc |> Yojson.Safe.Util.to_assoc + |> List.map (fun (key, value) -> + match (key, value) with + | "ledger", _ -> ("ledger", ledger) + | key, value -> (key, value)) + in + `List [ `String "Protocol"; `Assoc assoc ] |> Protocol.t_of_yojson + +let test_ticket_transfer_zero_ticket () = + let amount = Amount.of_n N.zero in + let level = Level.of_n N.one in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let ledger = + Ledger.initial |> Ledger.deposit bob_addr (Amount.of_n N.one) ticket_id + in + let initial_bob_balance = Ledger.balance bob_addr ticket_id ledger in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + Alcotest.(check amount') + "balance should not change" initial_bob_balance bob_balance + +let test_ticket_transfer_to_self () = + let level = Level.of_n N.one in + let amount = Amount.of_n N.one in + let payload = [ transfer ~level ~sender:bob ~receiver:bob_addr ~amount ] in + let ledger = + Ledger.initial |> Ledger.deposit bob_addr (Amount.of_n N.one) ticket_id + in + let initial_bob_balance = Ledger.balance bob_addr ticket_id ledger in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + Alcotest.(check amount') + "balance should not change" initial_bob_balance bob_balance + +let test_ticket_transfer_too_many_tickets () = + let level = Level.of_n N.one in + let amount = Amount.of_n (N.of_z (Z.of_int 32) |> Option.get) in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let ledger = + Ledger.initial |> Ledger.deposit bob_addr (Amount.of_n N.one) ticket_id + in + let initial_bob_balance = Ledger.balance bob_addr ticket_id ledger in + let initial_alice_balance = Ledger.balance alice_addr ticket_id ledger in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + let alice_balance = Ledger.balance alice_addr ticket_id ledger in + Alcotest.(check amount') + "balance of bob should not change" initial_bob_balance bob_balance; + Alcotest.(check amount') + "balance of alice &should not change" initial_alice_balance alice_balance + +let test_ticket_transfer_tickets () = + let level = Level.zero in + let amount = Amount.one in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let ledger = + let two = Z.of_int 2 |> N.of_z |> Option.get |> Amount.of_n in + Ledger.initial |> Ledger.deposit bob_addr two ticket_id + in + let initial_bob_balance = Ledger.balance bob_addr ticket_id ledger in + let initial_alice_balance = Ledger.balance alice_addr ticket_id ledger in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + let alice_balance = Ledger.balance alice_addr ticket_id ledger in + + let exepected_bob_balance = + Amount.(initial_bob_balance - amount) |> Option.get + in + let exepected_alice_balance = Amount.(initial_alice_balance + amount) in + + Alcotest.(check amount') + "balance should have descreased by 1 for bob" exepected_bob_balance + bob_balance; + Alcotest.(check amount') + "balance should have increased by 1 for alice" exepected_alice_balance + alice_balance + +let test_ticket_transfer_all_tickets () = + let level = Level.zero in + let amount = Z.of_int 2 |> N.of_z |> Option.get |> Amount.of_n in + let payload = [ transfer ~level ~sender:bob ~receiver:alice_addr ~amount ] in + let ledger = Ledger.initial |> Ledger.deposit bob_addr amount ticket_id in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + let alice_balance = Ledger.balance alice_addr ticket_id ledger in + Alcotest.(check amount') "balance should be 0 for bob" Amount.zero bob_balance; + Alcotest.(check amount') "balance should be 2 for alice" amount alice_balance + +(* Withdraw tests *) + +let withdraw ~level ~sender ~tezos_owner ~amount = + let nonce = Nonce.of_n N.one in + let transfer = + Operation.Signed.withdraw ~identity:sender ~nonce ~level ~tezos_owner + ~ticket_id ~amount + in + let (Operation.Signed.Signed_operation { initial; _ }) = transfer in + initial + +let test_withdraw_undefined_ticket () = + let level = Level.zero in + let amount = Amount.one in + let payload = + [ withdraw ~level ~sender:bob ~tezos_owner:bob_tezos ~amount ] + in + let protocol = Protocol.initial in + let _, _, exns = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + Alcotest.(check int) "exception should be raised" 1 (List.length exns) + +let test_withdraw_zero_ticket () = + let level = Level.zero in + let amount = Amount.zero in + let payload = + [ withdraw ~level ~sender:bob ~tezos_owner:bob_tezos ~amount ] + in + let ledger = Ledger.initial |> Ledger.deposit bob_addr amount ticket_id in + let protocol = protocol_with_ledger ~ledger in + let _, _, exns = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + Alcotest.(check int) "exception should be raised" 1 (List.length exns) + +let test_withdraw_too_many_tickets () = + let level = Level.zero in + let amount = 32 |> Z.of_int |> N.of_z |> Option.get |> Amount.of_n in + let payload = + [ withdraw ~level ~sender:bob ~tezos_owner:bob_tezos ~amount ] + in + let ledger = Ledger.initial |> Ledger.deposit bob_addr Amount.one ticket_id in + let protocol = protocol_with_ledger ~ledger in + let _, _, exns = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + Alcotest.(check int) "exception should be raised" 1 (List.length exns) + +let test_withdraw_all_tickets () = + let level = Level.zero in + let amount = 32 |> Z.of_int |> N.of_z |> Option.get |> Amount.of_n in + let payload = + [ withdraw ~level ~sender:bob ~tezos_owner:bob_tezos ~amount ] + in + let ledger = Ledger.initial |> Ledger.deposit bob_addr amount ticket_id in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + Alcotest.(check amount') "bob should have 0 tickets" Amount.zero bob_balance + +let test_withdraw_one_ticket () = + let level = Level.zero in + let amount = Amount.one in + let payload = + [ withdraw ~level ~sender:bob ~tezos_owner:bob_tezos ~amount ] + in + let ledger = + let amount = 32 |> Z.of_int |> N.of_z |> Option.get |> Amount.of_n in + Ledger.initial |> Ledger.deposit bob_addr amount ticket_id + in + let bob_initial_balance = Ledger.balance bob_addr ticket_id ledger in + let protocol = protocol_with_ledger ~ledger in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload ~tezos_operations:[] protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + let exepected_bob_balance = + Amount.(bob_initial_balance - amount) |> Option.get + in + Alcotest.(check amount') + "bob should have 0 tickets" exepected_bob_balance bob_balance + +(* Deposit tests *) +let deposit ~destination ~amount = + let destination = Address.to_key_hash destination |> Option.get in + let operation = + Tezos_operation.Deposit + { + destination; + amount; + ticket = Ticket_id.to_tezos_ticket ticket_id |> Option.get; + } + in + let hash = + Deku_tezos.Tezos_operation_hash.of_b58 + "ootmGcYJUiuuBir5MLnxKBYiGcdrHZoBp413obKQgUw6fNcLXd5" + |> Option.get + in + Tezos_operation.{ hash; operations = [ operation ] } + +let test_deposit_zero_ticket () = + let level = Level.zero in + let amount = Amount.zero in + let tezos_operations = [ deposit ~destination:bob_addr ~amount ] in + let protocol = Protocol.initial in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload:[] ~tezos_operations protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + Alcotest.(check amount') "balance should not change" Amount.zero bob_balance + +let test_deposit_tickets () = + let level = Level.zero in + let amount = Amount.one in + let tezos_operations = [ deposit ~destination:bob_addr ~amount ] in + let protocol = Protocol.initial in + let protocol, _, _ = + Protocol.apply ~current_level:level ~payload:[] ~tezos_operations protocol + in + let (Protocol.Protocol { ledger; _ }) = protocol in + let bob_balance = Ledger.balance bob_addr ticket_id ledger in + Alcotest.(check amount') "balance should be updated" amount bob_balance + let run () = let open Alcotest in run "Protocol" ~and_exit:false @@ -369,4 +738,55 @@ let run () = test_case "balance does not change, when not enough amount" `Quick test_cannot_create_amount_ex_nihilo; ] ); + ( "noop operation", + [ + test_case "noop operation should not create exceptions" `Quick + test_noop_operation_no_exceptions; + test_case "noop operation should not create receipts" `Quick + test_noop_operation_empty_receipts; + test_case "noop operation should not update the protocol" `Quick + test_noop_operation_does_nothing; + ] ); + ( "ticket transfer", + [ + test_case "cannot transfer undefined ticket" `Quick + test_ticket_transfer_undefined_ticket_exn; + test_case "undefined ticket transfer does not change the ledger" + `Quick test_ticket_transfer_undefined_ticket; + test_case "undefined ticket transfer returns a receipt " `Quick + test_ticket_transfer_undefined_ticket_receipt; + test_case "transfering 0 tickets have the same behaviour" `Quick + test_ticket_transfer_zero_ticket; + test_case "transfering tickets to self should change nothing" `Quick + test_ticket_transfer_to_self; + test_case + "transfering tickets too many tickets should not change the balance" + `Quick test_ticket_transfer_too_many_tickets; + test_case + "transfer ticket should decrease properly the balance of the \ + sender, and increase the balance of the receiver" + `Quick test_ticket_transfer_tickets; + test_case + "After transfering all his tickets bob should have 0 tickets" `Quick + test_ticket_transfer_all_tickets; + ] ); + ( "ticket withdraw", + [ + test_case "cannot withdraw undefined tickets" `Quick + test_withdraw_undefined_ticket; + test_case "cannot withdraw zero ticket" `Quick + test_withdraw_zero_ticket; + test_case "cannot withdraw more than balance" `Quick + test_withdraw_too_many_tickets; + test_case "can withdraw all tickets" `Quick test_withdraw_all_tickets; + test_case "can withdraw only one ticket" `Quick + test_withdraw_one_ticket; + ] ); + ( "ticket deposit", + [ + test_case "deposits 0 tickets does not affect the ledger" `Quick + test_deposit_zero_ticket; + test_case "deposits tickets update correctly the balance" `Quick + test_deposit_tickets; + ] ); ]