Client: add voting commands
tezos-client show votes to show the current voting status tezos-client submit proposals for PKH PROP1 .. to submit proposals tezos-client submit ballot for PKH PROP yay|nay|pass to submit a ballot
This commit is contained in:
parent
7e81fe6c9c
commit
e0a8718922
@ -42,6 +42,7 @@
|
||||
"time_between_blocks" : [ "1", "0" ],
|
||||
"blocks_per_roll_snapshot" : 4,
|
||||
"blocks_per_cycle" : 8,
|
||||
"blocks_per_voting_period" : 64,
|
||||
"preserved_cycles" : 2,
|
||||
"proof_of_work_threshold": "-1"
|
||||
}
|
||||
|
@ -140,6 +140,21 @@
|
||||
%{bin:tezos-client}
|
||||
%{bin:tezos-admin-client})))
|
||||
|
||||
(alias
|
||||
(name runtest_voting.sh)
|
||||
(locks /tcp-port/18731
|
||||
/tcp-port/19731)
|
||||
(deps sandbox.json
|
||||
test_lib.inc.sh
|
||||
(glob_files demo/*))
|
||||
(action
|
||||
(run bash %{dep:test_voting.sh}
|
||||
%{bin:tezos-sandboxed-node.sh}
|
||||
%{bin:tezos-node}
|
||||
%{bin:tezos-init-sandboxed-client.sh}
|
||||
%{bin:tezos-client}
|
||||
%{bin:tezos-admin-client})))
|
||||
|
||||
(alias
|
||||
(name runtest)
|
||||
(deps (alias runtest_basic.sh)
|
||||
@ -150,4 +165,5 @@
|
||||
(alias runtest_multinode.sh)
|
||||
(alias runtest_injection.sh)
|
||||
(alias runtest_tls.sh)
|
||||
(alias runtest_cors.sh)))
|
||||
(alias runtest_cors.sh)
|
||||
(alias runtest_voting.sh)))
|
||||
|
@ -21,6 +21,7 @@
|
||||
"time_between_blocks" : [ "1", "0" ],
|
||||
"blocks_per_cycle" : 128,
|
||||
"blocks_per_roll_snapshot" : 32,
|
||||
"blocks_per_voting_period" : 64,
|
||||
"preserved_cycles" : 1,
|
||||
"proof_of_work_threshold": "-1"
|
||||
}
|
||||
|
171
src/bin_client/test/test_voting.sh
Executable file
171
src/bin_client/test/test_voting.sh
Executable file
@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Requires jq command
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
test_dir="$(cd "$(dirname "$0")" && echo "$(pwd -P)")"
|
||||
source $test_dir/test_lib.inc.sh "$@"
|
||||
|
||||
# Prepare a config with shorter blocks_per_voting_period
|
||||
temp=`mktemp`
|
||||
sed -e 's/"blocks_per_voting_period" : [0-9]*/"blocks_per_voting_period" : 4/' $parameters_file > $temp
|
||||
parameters_file=$temp
|
||||
echo params=${parameters_file}
|
||||
|
||||
start_node 1
|
||||
activate_alpha
|
||||
|
||||
echo Alpha activated
|
||||
|
||||
res=`$client show votes`
|
||||
echo $res
|
||||
|
||||
[ `echo $res | jq .voting_period_position` = '1' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '3' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ `echo $res | jq .listings` = '[]' ] \
|
||||
|| { echo "empty listings bug was fixed?!" ; exit 1 ; }
|
||||
|
||||
bake # pos=2
|
||||
|
||||
res=`$client show votes`
|
||||
[ `echo $res | jq .voting_period_position` = '2' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '2' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
|
||||
bake # pos=3
|
||||
bake # new period, pos=0
|
||||
|
||||
res=`$client show votes`
|
||||
[ `echo $res | jq .voting_period_position` = '0' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '4' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ "`echo $res | jq .listings`" != '[]' ] \
|
||||
|| { echo "strange listings" ; exit 1 ; }
|
||||
|
||||
proto1='ProtoBetaBetaBetaBetaBetaBetaBetaBetaBet11111a5ug96'
|
||||
proto2='Proto222222222222222222222222222222222225b7e3dV844j'
|
||||
proto3='Proto33333333333333333333333333333333333c6379eVysnU'
|
||||
|
||||
$client submit proposals for bootstrap1 $proto1
|
||||
$client submit proposals for bootstrap2 $proto1 $proto2
|
||||
$client submit proposals for bootstrap3 $proto2
|
||||
$client submit proposals for bootstrap4 $proto3
|
||||
|
||||
bake
|
||||
|
||||
res=`$client show votes`
|
||||
[ "`echo $res | jq .proposals`" != '[]' ] \
|
||||
|| { echo "strange proposals" ; exit 1 ; }
|
||||
|
||||
bake # pos=2
|
||||
|
||||
echo Breaking the tie
|
||||
|
||||
$client submit proposals for bootstrap3 $proto1 # To make $proto1 win
|
||||
$client show votes
|
||||
|
||||
bake # pos=3
|
||||
bake # new period! pos=0
|
||||
|
||||
echo Proposal should be done
|
||||
res=`$client show votes`
|
||||
echo $res
|
||||
[ `echo $res | jq .voting_period_position` = '0' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '4' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_period_kind` = '"testing_vote"' ] \
|
||||
|| { echo "strange current_period_kind" ; exit 1 ; }
|
||||
[ "`echo $res | jq .listings`" != '[]' ] \
|
||||
|| { echo "strange listings" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_proposal` = '"'$proto1'"' ] \
|
||||
|| { echo "strange current_proposal" ; exit 1 ; }
|
||||
|
||||
echo Ballots 1
|
||||
$client submit ballot for bootstrap1 $proto1 yay
|
||||
$client submit ballot for bootstrap2 $proto1 yay
|
||||
$client submit ballot for bootstrap3 $proto1 yay
|
||||
$client submit ballot for bootstrap4 $proto1 yay
|
||||
|
||||
bake # pos=1
|
||||
|
||||
# They cannot change their mind.
|
||||
echo "Ballots 2 (should fail)"
|
||||
$client submit ballot for bootstrap1 $proto1 yay \
|
||||
&& { echo "submit ballot cannot be called twice" ; exit 1 ; }
|
||||
|
||||
bake # pos=2
|
||||
bake # pos=3
|
||||
|
||||
$client show votes
|
||||
|
||||
bake # new period pos=0
|
||||
|
||||
echo Testing vote should be done
|
||||
res=`$client show votes`
|
||||
[ `echo $res | jq .voting_period_position` = '0' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '4' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_period_kind` = '"testing"' ] \
|
||||
|| { echo "strange current_period_kind" ; exit 1 ; }
|
||||
[ "`echo $res | jq .listings`" = '[]' ] \
|
||||
|| { echo "strange listings" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_proposal` = '"'$proto1'"' ] \
|
||||
|| { echo "strange current_proposal" ; exit 1 ; }
|
||||
[ `echo $res | jq .ballot_list` = '[]' ] \
|
||||
|| { echo "strange ballot_list" ; exit 1 ; }
|
||||
|
||||
bake # pos=1
|
||||
bake # pos=2
|
||||
bake # pos=3
|
||||
bake # new period pos=0
|
||||
|
||||
echo Testing should be done
|
||||
res=`$client show votes`
|
||||
[ `echo $res | jq .voting_period_position` = '0' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '4' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_period_kind` = '"promotion_vote"' ] \
|
||||
|| { echo "strange current_period_kind" ; exit 1 ; }
|
||||
[ "`echo $res | jq .listings`" != '[]' ] \
|
||||
|| { echo "strange listings" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_proposal` = '"'$proto1'"' ] \
|
||||
|| { echo "strange current_proposal" ; exit 1 ; }
|
||||
[ `echo $res | jq .ballot_list` = '[]' ] \
|
||||
|| { echo "strange ballot_list" ; exit 1 ; }
|
||||
|
||||
$client submit ballot for bootstrap1 $proto1 yay
|
||||
$client submit ballot for bootstrap2 $proto1 yay
|
||||
$client submit ballot for bootstrap3 $proto1 yay
|
||||
$client submit ballot for bootstrap4 $proto1 nay # not to promote
|
||||
|
||||
bake # pos=1
|
||||
bake # pos=2
|
||||
bake # pos=3
|
||||
|
||||
$client show votes
|
||||
|
||||
bake # new period pos=0
|
||||
|
||||
echo 'Promotion vote should be done (negatively)'
|
||||
res=`$client show votes`
|
||||
[ `echo $res | jq .voting_period_position` = '0' ] \
|
||||
|| { echo "strange voting_period_position" ; exit 1 ; }
|
||||
[ `echo $res | jq .voting_period_remaining` = '4' ] \
|
||||
|| { echo "strange voting_period_remaining" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_period_kind` = '"proposal"' ] \
|
||||
|| { echo "strange current_period_kind" ; exit 1 ; }
|
||||
[ "`echo $res | jq .listings`" != '[]' ] \
|
||||
|| { echo "strange listings" ; exit 1 ; }
|
||||
[ `echo $res | jq .current_proposal` = 'null' ] \
|
||||
|| { echo "strange current_proposal" ; exit 1 ; }
|
||||
[ `echo $res | jq .ballot_list` = '[]' ] \
|
||||
|| { echo "strange ballot_list" ; exit 1 ; }
|
@ -420,6 +420,127 @@ let activate_existing_account
|
||||
| Some _ -> failwith "Only Ed25519 accounts can be activated"
|
||||
| None -> failwith "Unknown account"
|
||||
|
||||
type vote_info = {
|
||||
current_period_kind : Voting_period.kind ;
|
||||
voting_period_position : int32 ;
|
||||
voting_period_remaining : int32 ;
|
||||
current_quorum : Int32.t ;
|
||||
listings : (public_key_hash * int32) list ;
|
||||
(* The equality between Alpha_environment.Protocol_hash.t
|
||||
and Protocol_hash.t is dropped at Tezos_protocol_environment.Make(_).V1 *)
|
||||
proposals : Int32.t Alpha_environment.Protocol_hash.Map.t ;
|
||||
current_proposal : Protocol_hash.t option ;
|
||||
ballots : Vote.ballots ;
|
||||
ballot_list : (public_key_hash * Vote.ballot) list ;
|
||||
}
|
||||
|
||||
(* Should be moved to src/proto_alpha/lib_protocol/src/vote_storage.ml *)
|
||||
let ballot_list_encoding =
|
||||
Data_encoding.(list (obj2
|
||||
(req "pkh" Signature.Public_key_hash.encoding)
|
||||
(req "balllot" Vote.ballot_encoding)))
|
||||
|
||||
let vote_info_encoding =
|
||||
let open Data_encoding in
|
||||
conv
|
||||
(fun { current_period_kind ;
|
||||
voting_period_position ;
|
||||
voting_period_remaining ;
|
||||
current_quorum ;
|
||||
listings ;
|
||||
proposals ;
|
||||
current_proposal ;
|
||||
ballots ;
|
||||
ballot_list } ->
|
||||
( current_period_kind ,
|
||||
voting_period_position ,
|
||||
voting_period_remaining ,
|
||||
current_quorum ,
|
||||
listings ,
|
||||
proposals ,
|
||||
current_proposal ,
|
||||
ballots ,
|
||||
ballot_list ))
|
||||
(fun ( current_period_kind ,
|
||||
voting_period_position ,
|
||||
voting_period_remaining ,
|
||||
current_quorum ,
|
||||
listings ,
|
||||
proposals ,
|
||||
current_proposal ,
|
||||
ballots ,
|
||||
ballot_list ) ->
|
||||
{ current_period_kind ;
|
||||
voting_period_position ;
|
||||
voting_period_remaining ;
|
||||
current_quorum ;
|
||||
listings ;
|
||||
proposals ;
|
||||
current_proposal ;
|
||||
ballots ;
|
||||
ballot_list })
|
||||
@@ obj9
|
||||
(req "current_period_kind" Voting_period.kind_encoding)
|
||||
(req "voting_period_position" Data_encoding.int32)
|
||||
(req "voting_period_remaining" Data_encoding.int32)
|
||||
(req "current_quorum" Data_encoding.int32)
|
||||
(req "listings" Vote.listings_encoding)
|
||||
(req "proposals" (Alpha_environment.Protocol_hash.Map.encoding Data_encoding.int32))
|
||||
(req "current_proposal" (Data_encoding.option Protocol_hash.encoding))
|
||||
(req "ballots" Vote.ballots_encoding)
|
||||
(req "ballot_list" ballot_list_encoding)
|
||||
|
||||
let get_vote_info
|
||||
(cctxt : #Proto_alpha.full)
|
||||
~chain ~block =
|
||||
(* Get the next level, not the current *)
|
||||
let cb = (chain, block) in
|
||||
Alpha_services.Helpers.current_level cctxt ~offset:1l cb >>=? fun level ->
|
||||
Alpha_services.Constants.all cctxt cb >>=? fun constants ->
|
||||
let voting_period_position = level.voting_period_position in
|
||||
let voting_period_remaining =
|
||||
Int32.(sub constants.parametric.blocks_per_voting_period voting_period_position) in
|
||||
Alpha_services.Voting.ballots cctxt cb >>=? fun ballots ->
|
||||
Alpha_services.Voting.ballot_list cctxt cb >>=? fun ballot_list ->
|
||||
Alpha_services.Voting.current_period_kind cctxt cb >>=? fun current_period_kind ->
|
||||
Alpha_services.Voting.current_quorum cctxt cb >>=? fun current_quorum ->
|
||||
Alpha_services.Voting.listings cctxt cb >>=? fun listings ->
|
||||
Alpha_services.Voting.proposals cctxt cb >>=? fun proposals ->
|
||||
Alpha_services.Voting.current_proposal cctxt cb >>=? fun current_proposal ->
|
||||
return { voting_period_position ;
|
||||
voting_period_remaining ;
|
||||
ballots ;
|
||||
ballot_list ;
|
||||
current_period_kind ;
|
||||
current_quorum ;
|
||||
listings ;
|
||||
proposals ;
|
||||
current_proposal }
|
||||
|
||||
let submit_proposals
|
||||
(cctxt : #Proto_alpha.full)
|
||||
~chain ~block ?confirmations ~src_sk source proposals =
|
||||
(* We need the next level, not the current *)
|
||||
Alpha_services.Helpers.current_level cctxt ~offset:1l (chain, block) >>=? fun (level : Level.t) ->
|
||||
let period = level.voting_period in
|
||||
let contents = Single ( Proposals { source ; period ; proposals } ) in
|
||||
Injection.inject_operation cctxt ~chain ~block ?confirmations
|
||||
~fee_parameter:Injection.dummy_fee_parameter
|
||||
~src_sk contents
|
||||
|
||||
let submit_ballot
|
||||
(cctxt : #Proto_alpha.full)
|
||||
~chain ~block ?confirmations ~src_sk source proposal ballot =
|
||||
(* The user must provide the proposal explicitly to make himself sure
|
||||
for what he is voting.
|
||||
*)
|
||||
Alpha_services.Helpers.current_level cctxt ~offset:1l (chain, block) >>=? fun (level : Level.t) ->
|
||||
let period = level.voting_period in
|
||||
let contents = Single ( Ballot { source ; period ; proposal ; ballot } ) in
|
||||
Injection.inject_operation cctxt ~chain ~block ?confirmations
|
||||
~fee_parameter:Injection.dummy_fee_parameter
|
||||
~src_sk contents
|
||||
|
||||
let pp_operation formatter (a : Alpha_block_services.operation) =
|
||||
match a.receipt, a.protocol_data with
|
||||
| Apply_results.Operation_metadata omd, Operation_data od -> (
|
||||
|
@ -218,6 +218,37 @@ val activate_existing_account:
|
||||
Blinded_public_key_hash.activation_code ->
|
||||
Kind.activate_account Injection.result tzresult Lwt.t
|
||||
|
||||
type vote_info
|
||||
|
||||
val vote_info_encoding : vote_info Data_encoding.t
|
||||
|
||||
val get_vote_info :
|
||||
#Proto_alpha.full ->
|
||||
chain:Shell_services.chain ->
|
||||
block:Shell_services.block ->
|
||||
vote_info tzresult Lwt.t
|
||||
|
||||
val submit_proposals:
|
||||
#Proto_alpha.full ->
|
||||
chain:Shell_services.chain ->
|
||||
block:Shell_services.block ->
|
||||
?confirmations:int ->
|
||||
src_sk:Client_keys.sk_uri ->
|
||||
public_key_hash ->
|
||||
Protocol_hash.t list ->
|
||||
Kind.proposals Injection.result_list tzresult Lwt.t
|
||||
|
||||
val submit_ballot:
|
||||
#Proto_alpha.full ->
|
||||
chain:Shell_services.chain ->
|
||||
block:Shell_services.block ->
|
||||
?confirmations:int ->
|
||||
src_sk:Client_keys.sk_uri ->
|
||||
public_key_hash ->
|
||||
Protocol_hash.t ->
|
||||
Proto_alpha.Alpha_context.Vote.ballot ->
|
||||
Kind.ballot Injection.result_list tzresult Lwt.t
|
||||
|
||||
(** lookup an operation in [predecessors] previous blocks, and print the
|
||||
receipt if found *)
|
||||
val display_receipt_for_operation:
|
||||
|
@ -400,6 +400,7 @@ let rec pp_contents_and_result_list :
|
||||
Signature.Public_key_hash.pp source
|
||||
Voting_period.pp period
|
||||
Protocol_hash.pp proposal
|
||||
(* FIXME We should use ballot_encoding? *)
|
||||
(match ballot with Yay -> "YAY" | Pass -> "PASS" | Nay -> "NAY")
|
||||
| Single_and_result (Manager_operation _ as op,
|
||||
(Manager_operation_result _ as res))->
|
||||
|
@ -641,6 +641,73 @@ let commands version () =
|
||||
(Data_encoding.Binary.describe
|
||||
Alpha_context.Operation.unsigned_encoding) >>= fun () ->
|
||||
return_unit
|
||||
end
|
||||
end ;
|
||||
|
||||
command ~group ~desc: "Submit protocol proposals."
|
||||
no_options
|
||||
(prefixes [ "submit" ; "proposals" ]
|
||||
@@ prefix "for"
|
||||
@@ ContractAlias.destination_param
|
||||
~name: "src" ~desc: "name of the source contract"
|
||||
@@ seq_of_param (param
|
||||
~name:"proposal"
|
||||
~desc:"Proposal to be submitted"
|
||||
(parameter
|
||||
(fun _ x ->
|
||||
match Protocol_hash.of_b58check_opt x with
|
||||
| None -> Error_monad.failwith "Invalid proposal hash: '%s'" x
|
||||
| Some hash -> return hash)))
|
||||
)
|
||||
(fun () (_name, source) proposals cctxt ->
|
||||
Client_proto_context.get_manager
|
||||
cctxt ~chain:`Main ~block:cctxt#block
|
||||
source >>=? fun (_src_name, src_pkh, _src_pk, src_sk) ->
|
||||
submit_proposals cctxt ~chain:`Main ~block:cctxt#block ~src_sk src_pkh proposals >>=? fun _res ->
|
||||
return_unit
|
||||
);
|
||||
|
||||
command ~group ~desc: "Submit a ballot."
|
||||
no_options
|
||||
(prefixes [ "submit" ; "ballot" ]
|
||||
@@ prefix "for"
|
||||
@@ ContractAlias.destination_param
|
||||
~name: "src" ~desc: "name of the source contract"
|
||||
@@ param
|
||||
~name:"proposal"
|
||||
~desc:"Proposal"
|
||||
(parameter
|
||||
(fun _ x ->
|
||||
match Protocol_hash.of_b58check_opt x with
|
||||
| None -> Error_monad.failwith "Invalid proposal hash: '%s'" x
|
||||
| Some hash -> return hash))
|
||||
@@ param
|
||||
~name:"ballot"
|
||||
~desc:"Ballot(yay/nay/pass)"
|
||||
(parameter
|
||||
(fun _ s ->
|
||||
let fail () = Error_monad.failwith "Invalid ballot: '%s'" s in
|
||||
match Data_encoding.Json.from_string ("\"" ^ s ^ "\"") with
|
||||
| Error _ -> fail ()
|
||||
| Ok j ->
|
||||
match Data_encoding.Json.destruct Vote.ballot_encoding j with
|
||||
| exception _ -> fail ()
|
||||
| b -> return b))
|
||||
@@ stop
|
||||
)
|
||||
(fun () (_name, source) proposal ballot cctxt ->
|
||||
Client_proto_context.get_manager
|
||||
cctxt ~chain:`Main ~block:cctxt#block
|
||||
source >>=? fun (_src_name, src_pkh, _src_pk, src_sk) ->
|
||||
submit_ballot cctxt ~chain:`Main ~block:cctxt#block ~src_sk src_pkh proposal ballot >>=? fun _res ->
|
||||
return_unit
|
||||
);
|
||||
|
||||
command ~group ~desc: "Summarize the current voting information."
|
||||
no_options
|
||||
(fixed [ "show" ; "votes" ])
|
||||
(fun () cctxt ->
|
||||
get_vote_info ~chain:`Main ~block:cctxt#block cctxt >>=? fun vote_info ->
|
||||
cctxt#message "%a" (Json_repr.pp_any ()) (Json_repr.(to_any (Data_encoding.Json.construct vote_info_encoding vote_info))) >>= fun () ->
|
||||
return_unit
|
||||
)
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user