diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d2f2a570..9c1da8597 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -132,6 +132,15 @@ test:proto_alpha:endorsement: dependencies: - build +test:proto_alpha:vote: + stage: test + tags: + - tezos_builder + script: + - make -C test/proto_alpha run-test-vote + dependencies: + - build + test:basic.sh: stage: test tags: diff --git a/src/client/embedded/alpha/client_proto_rpcs.ml b/src/client/embedded/alpha/client_proto_rpcs.ml index af876e697..ca3dccc02 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.ml +++ b/src/client/embedded/alpha/client_proto_rpcs.ml @@ -218,6 +218,14 @@ module Helpers = struct b ~net ~source ~block ~slot () = operations cctxt b ~net ~source Tezos_context.[Endorsement { block ; slot }] + let proposals cctxt + b ~net ~source ~period ~proposals () = + operations cctxt b ~net ~source + Tezos_context.[Proposals { period ; proposals }] + let ballot cctxt + b ~net ~source ~period ~proposal ~ballot () = + operations cctxt b ~net ~source + Tezos_context.[Ballot { period ; proposal ; ballot }] end module Dictator = struct let operation cctxt diff --git a/src/client/embedded/alpha/client_proto_rpcs.mli b/src/client/embedded/alpha/client_proto_rpcs.mli index 6e0d712b3..61f571cfc 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.mli +++ b/src/client/embedded/alpha/client_proto_rpcs.mli @@ -269,6 +269,23 @@ module Helpers : sig block:Block_hash.t -> slot:int -> unit -> MBytes.t tzresult Lwt.t + val proposals: + Client_rpcs.config -> + block -> + net:Net_id.t -> + source:public_key -> + period:Voting_period.t -> + proposals:Hash.Protocol_hash.t list -> + unit -> MBytes.t tzresult Lwt.t + val ballot: + Client_rpcs.config -> + block -> + net:Net_id.t -> + source:public_key -> + period:Voting_period.t -> + proposal:Hash.Protocol_hash.t -> + ballot:Vote.ballot -> + unit -> MBytes.t tzresult Lwt.t end module Anonymous : sig val operations: diff --git a/test/proto_alpha/Makefile b/test/proto_alpha/Makefile index be41183f8..90f02bb23 100644 --- a/test/proto_alpha/Makefile +++ b/test/proto_alpha/Makefile @@ -5,6 +5,7 @@ TESTS := \ transaction \ origination \ endorsement \ + vote \ include ../Makefile.shared @@ -85,3 +86,22 @@ test-endorsement: ${LIB} ${TEST_CONNECTION_IMPLS:.ml=.cmx} clean:: rm -f test-endorsement +############################################################################ +## Vote + +.PHONY:run-test-vote +run-test-vote: + @echo + ./test-vote + +TEST_CONNECTION_IMPLS := \ + proto_alpha_helpers.ml \ + test_vote.ml + +test-vote: ${LIB} ${TEST_CONNECTION_IMPLS:.ml=.cmx} + @echo COMPILE $(notdir $@) + @${OCAMLOPT} -linkall -linkpkg ${OCAMLFLAGS} -o $@ $^ + +clean:: + rm -f test-vote + diff --git a/test/proto_alpha/proto_alpha_helpers.ml b/test/proto_alpha/proto_alpha_helpers.ml index 798e1da19..621ffb3a2 100644 --- a/test/proto_alpha/proto_alpha_helpers.ml +++ b/test/proto_alpha/proto_alpha_helpers.ml @@ -32,17 +32,20 @@ let activate_alpha () = (Activate Client_proto_main.protocol) fitness dictator_sk -let init () = +let init ?(sandbox = "sandbox.json") () = Random.self_init () ; Unix.chdir (Filename.dirname (Filename.dirname Sys.executable_name)) ; let pid = Node_helpers.fork_node ~port:rpc_config.port - ~sandbox:(Filename.dirname Sys.executable_name // "sandbox.json") + ~sandbox:(Filename.dirname Sys.executable_name // sandbox) () in activate_alpha () >>=? fun hash -> return (pid, hash) +let level block = + Client_alpha.Client_proto_rpcs.Context.level rpc_config block + module Account = struct type t = { @@ -237,6 +240,41 @@ module Account = struct end +module Protocol = struct + + open Account + + let inject_proposals ?async ?force ?(block = `Prevalidation) ~src:({ pk; sk } : Account.t) proposals = + Client_node_rpcs.Blocks.info rpc_config block >>=? fun block_info -> + Client_proto_rpcs.Context.next_level rpc_config block >>=? fun next_level -> + Client_proto_rpcs.Helpers.Forge.Delegate.proposals rpc_config block + ~net:block_info.net_id + ~source:pk + ~period:next_level.voting_period + ~proposals + () >>=? fun bytes -> + let signed_bytes = Environment.Ed25519.Signature.append sk bytes in + Client_node_rpcs.inject_operation + rpc_config ?async ?force signed_bytes >>=? fun oph -> + return oph + + let inject_ballot ?async ?force ?(block = `Prevalidation) ~src:({ pk; sk } : Account.t) ~proposal ballot = + Client_node_rpcs.Blocks.info rpc_config block >>=? fun block_info -> + Client_proto_rpcs.Context.next_level rpc_config block >>=? fun next_level -> + Client_proto_rpcs.Helpers.Forge.Delegate.ballot rpc_config block + ~net:block_info.net_id + ~source:pk + ~period:next_level.voting_period + ~proposal + ~ballot + () >>=? fun bytes -> + let signed_bytes = Environment.Ed25519.Signature.append sk bytes in + Client_node_rpcs.inject_operation + rpc_config ?async ?force signed_bytes >>=? fun oph -> + return oph + +end + module Assert = struct include Assert @@ -335,6 +373,14 @@ module Assert = struct | _ -> false) end + let check_protocol ?msg ~block h = + Client_node_rpcs.Blocks.protocol rpc_config block >>=? fun block_proto -> + return @@ Assert.equal + ?msg:(Assert.format_msg msg) + ~prn:Protocol_hash.to_b58check + ~eq:Protocol_hash.equal + block_proto h + end module Mining = struct @@ -383,6 +429,7 @@ module Mining = struct let inject_block block ?force + ?proto_level ~priority ~timestamp ~fitness @@ -391,6 +438,7 @@ module Mining = struct operation_list = let block = match block with `Prevalidation -> `Head 0 | block -> block in Client_node_rpcs.Blocks.info rpc_config block >>=? fun bi -> + let proto_level = Utils.unopt ~default:bi.proto_level proto_level in let seed_nonce_hash = Nonce.hash seed_nonce in Client_proto_rpcs.Context.next_level rpc_config block >>=? fun level -> let operations_hash = @@ -400,7 +448,7 @@ module Mining = struct { Store.Block_header.net_id = bi.net_id ; predecessor = bi.hash ; timestamp ; fitness ; operations_hash ; level = Raw_level.to_int32 level.level ; - proto_level = 1 } in + proto_level } in mine_stamp block src_sk shell priority seed_nonce_hash >>=? fun proof_of_work_nonce -> Client_proto_rpcs.Helpers.Forge.block rpc_config @@ -411,7 +459,7 @@ module Mining = struct ~fitness ~operations_hash ~level:level.level - ~proto_level:1 + ~proto_level ~priority ~seed_nonce_hash ~proof_of_work_nonce @@ -424,7 +472,8 @@ module Mining = struct let mine ?(force = false) ?(operations = []) - ~fitness_gap + ?(fitness_gap = 1) + ?proto_level contract block = Client_mining_blocks.info rpc_config block >>=? fun bi -> @@ -444,6 +493,7 @@ module Mining = struct Int64.add fitness (Int64.of_int fitness_gap) in inject_block ~force + ?proto_level ~priority ~timestamp ~fitness diff --git a/test/proto_alpha/proto_alpha_helpers.mli b/test/proto_alpha/proto_alpha_helpers.mli index 00543b498..c9c4eada5 100644 --- a/test/proto_alpha/proto_alpha_helpers.mli +++ b/test/proto_alpha/proto_alpha_helpers.mli @@ -11,11 +11,13 @@ open Client_embedded_proto_alpha open Tezos_context open Client_alpha -val init : unit -> (int * Block_hash.t) tzresult Lwt.t +val init : ?sandbox:string -> unit -> (int * Block_hash.t) tzresult Lwt.t (** [init ()] sets up the test environment, and return the PID of forked Tezos node and the block info of the block from where the tests will begin. *) +val level : Client_proto_rpcs.block -> Tezos_context.Level.t tzresult Lwt.t + module Account : sig type t = { @@ -112,6 +114,7 @@ module Mining : sig val inject_block : Client_node_rpcs.Blocks.block -> ?force:bool -> + ?proto_level:int -> priority:int -> timestamp:Time.t -> fitness:Fitness.t -> @@ -122,7 +125,8 @@ module Mining : sig val mine : ?force:bool -> ?operations:Operation_hash.t list -> - fitness_gap:int -> + ?fitness_gap:int -> + ?proto_level:int -> Account.t -> Client_node_rpcs.Blocks.block -> Block_hash.t tzresult Lwt.t @@ -155,6 +159,27 @@ module Endorse : sig end +module Protocol : sig + + val inject_proposals : + ?async:bool -> + ?force:bool -> + ?block:Client_node_rpcs.Blocks.block -> + src:Account.t -> + Hash.Protocol_hash.t list -> + Hash.Operation_list_hash.elt tzresult Lwt.t + + val inject_ballot : + ?async:bool -> + ?force:bool -> + ?block:Client_node_rpcs.Blocks.block -> + src:Account.t -> + proposal:Hash.Protocol_hash.t -> + Vote.ballot -> + Hash.Operation_list_hash.elt tzresult Lwt.t + +end + module Assert : sig include module type of Assert @@ -191,6 +216,10 @@ module Assert : sig val invalid_endorsement_slot : msg:string -> 'a tzresult -> unit + val check_protocol : + ?msg:string -> block:Client_node_rpcs.Blocks.block -> + Hash.Protocol_hash.t -> unit tzresult Lwt.t + end val rpc_config: Client_rpcs.config diff --git a/test/proto_alpha/sandbox-vote.json b/test/proto_alpha/sandbox-vote.json new file mode 100644 index 000000000..c6fba56c8 --- /dev/null +++ b/test/proto_alpha/sandbox-vote.json @@ -0,0 +1,16 @@ +{ + "genesis_pubkey": + "edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2", + "bootstrap_keys": [ + "edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav", + "edpktzNbDAUjUk697W7gYg2CRuBQjyPxbEg8dLccYYwKSKvkPvjtV9", + "edpkuTXkJDGcFd5nh6VvMz8phXxU3Bi7h6hqgywNFi1vZTfQNnS1RV", + "edpkuFrRoDSEbJYgxRtLx2ps82UdaYc1WwfS9sE11yhauZt5DgCHbU", + "edpkv8EUUH68jmo3f7Um5PezmfGrRF24gnfLpH3sVNwJnV5bVCxL2n" + ], + "slot_durations" : [ 1, 0 ], + "cycle_length" : 4, + "voting_period_length" : 2, + "time_before_reward" : 1, + "first_free_mining_slot" : 4 +} diff --git a/test/proto_alpha/test_vote.ml b/test/proto_alpha/test_vote.ml new file mode 100644 index 000000000..f153acc8f --- /dev/null +++ b/test/proto_alpha/test_vote.ml @@ -0,0 +1,90 @@ +(**************************************************************************) +(* *) +(* Copyright (c) 2014 - 2016. *) +(* Dynamic Ledger Solutions, Inc. *) +(* *) +(* All rights reserved. No warranty, explicit or implicit, provided. *) +(* *) +(**************************************************************************) + +open Client_embedded_proto_alpha +open Tezos_context +open Proto_alpha_helpers + +let demo_protocol = + Protocol_hash.of_b58check_exn + "ProtoDemoDemoDemoDemoDemoDemoDemoDemoDemoDemoD3c8k9" + +let print_level head = + level (`Hash head) >>=? fun lvl -> + return @@ Format.eprintf "voting_period = %a.%ld@." + Voting_period.pp lvl.voting_period lvl.voting_period_position + +let run_change_to_demo_proto block ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstrap_accounts) = + Mining.mine b1 block >>=? fun head -> + Format.eprintf "Entering `Proposal` voting period@."; + Mining.mine b2 (`Hash head) >>=? fun head -> + + (* 1. Propose the 'demo' protocol as b1 (during the Proposal period) *) + Protocol.inject_proposals + ~force:true + ~block:(`Hash head) + ~src:b1 + [demo_protocol] >>=? fun oph -> + + (* Mine blocks to switch to next vote period (Testing_vote) *) + Mining.mine ~operations:[oph] b3 (`Hash head) >>=? fun head -> + Format.eprintf "Entering `Testing_vote` voting period@."; + Mining.mine b4 (`Hash head) >>=? fun head -> + + (* 2. Vote unanimously for a proposal *) + + let vote_for_demo ~src ~block ballot = + Protocol.inject_ballot + ~force:true + ~block + ~src + ~proposal:demo_protocol + ballot + in + let all_accounts = [b1; b2; b3; b4; b5] in + + map_s (fun src -> vote_for_demo ~src ~block:(`Hash head) Vote.Yay) + all_accounts >>=? fun operations -> + + (* Mine blocks to switch to next vote period (Testing) *) + Mining.mine ~operations b5 (`Hash head) >>=? fun head -> + Format.eprintf "Entering `Testing` voting period@."; + Mining.mine b1 (`Hash head) >>=? fun head -> + + (* 3. Test the proposed protocol *) + + (* Mine blocks to switch to next vote period (Promote_vote) *) + Mining.mine b2 (`Hash head) >>=? fun head -> + Format.eprintf "Entering `Promote_vote` voting period@."; + Mining.mine b3 (`Hash head) >>=? fun head -> + + (* 4. Vote unanimously for promoting the protocol *) + map_s (fun src -> vote_for_demo ~src ~block:(`Hash head) Vote.Yay) + all_accounts >>=? fun operations -> + + (* Mine blocks to switch to end the vote cycle (back to Proposal) *) + Format.eprintf "Switching to `demo` protocol@."; + Mining.mine ~operations b4 (`Hash head) >>=? fun head -> + Mining.mine ~proto_level:2 b5 (`Hash head) >>=? fun head -> + + Assert.check_protocol ~msg:__LOC__ ~block:(`Hash head) demo_protocol >>=? fun () -> + + return (`Hash head) + +let change_to_demo_proto () = + init ~sandbox:"sandbox-vote.json" () >>=? fun (_node_pid, hash) -> + run_change_to_demo_proto (`Hash hash) Account.bootstrap_accounts >>=? fun _blkh -> + return () + +let tests = [ + "change_to_demo_proto", (fun _ -> change_to_demo_proto ()) ; +] + +let () = + Test.run "amendment." tests diff --git a/test/proto_alpha/test_vote.mli b/test/proto_alpha/test_vote.mli new file mode 100644 index 000000000..76a0bb6b7 --- /dev/null +++ b/test/proto_alpha/test_vote.mli @@ -0,0 +1,8 @@ +(**************************************************************************) +(* *) +(* Copyright (c) 2014 - 2016. *) +(* Dynamic Ledger Solutions, Inc. *) +(* *) +(* All rights reserved. No warranty, explicit or implicit, provided. *) +(* *) +(**************************************************************************)