Commit 8fb2a8f1 authored by Stephane Glondu's avatar Stephane Glondu

Initial commit

parents
((nil . ((eval . (setq default-directory (locate-dominating-file
buffer-file-name
".dir-locals.el"))))))
*~
_build
#use "topfind";;
#require "zarith";;
#require "calendar";;
#require "uuidm";;
#require "atdgen";;
#require "yojson";;
#directory "_build/src";;
#load "lib.cma";;
let pp_print_datetime ppf x =
Format.pp_print_string ppf (Core_datatypes_j.string_of_datetime x)
;;
let pp_print_uuid ppf x =
Format.pp_print_string ppf (Core_datatypes_j.string_of_uuid x)
;;
#install_printer Z.pp_print;;
#install_printer pp_print_datetime;;
#install_printer pp_print_uuid;;
This diff is collapsed.
all:
ocamlbuild all.otarget
clean:
ocamlbuild -clean
rm -f *~
<**/*.{ml,mli}>: package(zarith), package(calendar), package(uuidm)
<**/*_{t,j}.{ml,mli}>: package(atdgen), package(yojson)
src/lib.cma
open Ocamlbuild_plugin
let atdgen_action opts env build =
let x = env "%.atd" in
let d = Pathname.dirname x and f = Pathname.basename x in
Cmd (S [A"cd"; P d; Sh"&&"; A"atdgen"; S opts; P f])
let () = dispatch & function
| Before_options ->
Options.use_ocamlfind := true;
Options.make_links := false;
| After_rules ->
rule "%.atd -> %_t.ml & %_t.mli" ~deps:["%.atd"] ~prods:["%_t.ml"; "%_t.mli"]
(atdgen_action [A"-t"]);
rule "%.atd -> %_j.ml & %_j.mli" ~deps:["%.atd"] ~prods:["%_j.ml"; "%_j.mli"]
(atdgen_action [A"-j"; A"-j-std"]);
| _ -> ()
open Core_datatypes_t
(** {1 Serializers for type number} *)
let write_number buf n =
Bi_outbuf.add_char buf '"';
Bi_outbuf.add_string buf (Z.to_string n);
Bi_outbuf.add_char buf '"'
let string_of_number ?(len=2048) n =
let buf = Bi_outbuf.create len in
write_number buf n;
Bi_outbuf.contents buf
let number_of_json = function
| `String s -> Z.of_string s
| _ -> assert false
let read_number state buf =
number_of_json (Yojson.Safe.from_lexbuf ~stream:true state buf)
let number_of_string s =
number_of_json (Yojson.Safe.from_string s)
(** {1 Serializers for type uuid} *)
let write_uuid buf n =
Bi_outbuf.add_char buf '"';
Bi_outbuf.add_string buf (Uuidm.to_string n);
Bi_outbuf.add_char buf '"'
let string_of_uuid ?(len=38) n =
let buf = Bi_outbuf.create len in
write_uuid buf n;
Bi_outbuf.contents buf
let uuid_of_json = function
| `String s ->
(match Uuidm.of_string s with Some s -> s | _ -> assert false)
| _ -> assert false
let read_uuid state buf =
uuid_of_json (Yojson.Safe.from_lexbuf ~stream:true state buf)
let uuid_of_string s =
uuid_of_json (Yojson.Safe.from_string s)
(** {1 Serializers for type datetime} *)
open CalendarLib
let datetime_format = "%Y-%m-%d %H:%M:%S"
let write_datetime buf n =
Bi_outbuf.add_char buf '"';
Bi_outbuf.add_string buf (Printer.Precise_Fcalendar.sprint datetime_format n);
let ts = Printf.sprintf "%.6f" (Fcalendar.Precise.to_unixfloat n) in
let i = String.index ts '.' in
Bi_outbuf.add_substring buf ts i (String.length ts - i);
Bi_outbuf.add_char buf '"'
let string_of_datetime ?(len=28) n =
let buf = Bi_outbuf.create len in
write_datetime buf n;
Bi_outbuf.contents buf
let datetime_of_json = function
| `String s ->
let i = String.index s '.' in
let l = Printer.Precise_Fcalendar.from_fstring datetime_format (String.sub s 0 i) in
let r = float_of_string ("0" ^ String.sub s i (String.length s-i)) in
Fcalendar.Precise.add l (Fcalendar.Precise.Period.second r)
| _ -> assert false
let read_datetime state buf =
datetime_of_json (Yojson.Safe.from_lexbuf ~stream:true state buf)
let datetime_of_string s =
datetime_of_json (Yojson.Safe.from_string s)
open Core_datatypes_t
(** {1 Serializers for type number} *)
val write_number : Bi_outbuf.t -> number -> unit
val string_of_number : ?len:int -> number -> string
val read_number : Yojson.Safe.lexer_state -> Lexing.lexbuf -> number
val number_of_string : string -> number
(** {1 Serializers for type uuid} *)
val write_uuid : Bi_outbuf.t -> uuid -> unit
val string_of_uuid : ?len:int -> uuid -> string
val read_uuid : Yojson.Safe.lexer_state -> Lexing.lexbuf -> uuid
val uuid_of_string : string -> uuid
(** {1 Serializers for type datetime} *)
val write_datetime : Bi_outbuf.t -> datetime -> unit
val string_of_datetime : ?len:int -> datetime -> string
val read_datetime : Yojson.Safe.lexer_state -> Lexing.lexbuf -> datetime
val datetime_of_string : string -> datetime
type number = Z.t
type uuid = Uuidm.t
type datetime = CalendarLib.Fcalendar.Precise.t
(** {1 Core types} *)
type number <ocaml predef from="Core_datatypes"> = abstract
type uuid <ocaml predef from="Core_datatypes"> = abstract
type datetime <ocaml predef from="Core_datatypes"> = abstract
(** {1 ElGamal keys} *)
type public_key = {
g : number;
p : number;
q : number;
y : number;
}
type private_key = {
public_key : public_key;
x : number;
}
(** {1 Elections} *)
type tally_type = [
| Homomorphic <json name="homomorphic">
]
type choice_type = [
| Approval <json name="approval">
]
type result_type = [
| Absolute <json name="absolute">
| Relative <json name="relative">
]
type question = {
answer_urls : unit list;
answers : string list;
choice_type : choice_type;
max : int;
min : int;
question : string;
result_type : result_type;
short_name : string;
tally_type : tally_type;
} <ocaml field_prefix="q_">
type election = {
cast_url : string;
description : string;
frozen_at : datetime;
name : string;
openreg : bool;
public_key : public_key;
questions : question list;
uuid : uuid;
short_name : string;
use_voter_aliases : bool;
voters_hash : unit;
voting_ends_at : unit;
voting_starts_at : unit;
} <ocaml field_prefix="e_">
(** {1 Ballots} *)
type choice = {
alpha : number;
beta : number;
}
type commitment = {
a <json name="A"> : number;
b <json name="B"> : number;
}
type proof_item = {
challenge : number;
commitment : commitment;
response : number;
}
type proof = proof_item list
type answer = {
choices : choice list;
individual_proofs : proof list;
overall_proof : proof;
}
type vote = {
answers : answer list;
election_hash : string;
election_uuid : uuid;
}
(** {1 Tally} *)
type tally = choice list list
type encrypted_tally = {
num_tallied : int;
tally : tally;
}
(** {1 Partial decryptions} *)
type partial_decryption = {
decryption_factors : number list list;
decryption_proofs : proof list;
}
Core_datatypes_j
Helios_datatypes_t
Helios_datatypes_j
{"cast_url": "http://localhost:8000/helios/elections/d341fe6e-0bcf-11e2-8c73-3cd92b7981b8/cast", "description": "lorem ipsum", "frozen_at": "2012-10-01 14:15:59.725606", "name": "What is the best editor?", "openreg": true, "public_key": {"g": "14887492224963187634282421537186040801304008017743492304481737382571933937568724473847106029915040150784031882206090286938661464458896494215273989547889201144857352611058572236578734319505128042602372864570426550855201448111746579871811249114781674309062693442442368697449970648232621880001709535143047913661432883287150003429802392229361583608686643243349727791976247247948618930423866180410558458272606627111270040091203073580238905303994472202930783207472394578498507764703191288249547659899997131166130259700604433891232298182348403175947450284433411265966789131024573629546048637848902243503970966798589660808533", "p": "16328632084933010002384055033805457329601614771185955389739167309086214800406465799038583634953752941675645562182498120750264980492381375579367675648771293800310370964745767014243638518442553823973482995267304044326777047662957480269391322789378384619428596446446984694306187644767462460965622580087564339212631775817895958409016676398975671266179637898557687317076177218843233150695157881061257053019133078545928983562221396313169622475509818442661047018436264806901023966236718367204710755935899013750306107738002364137917426595737403871114187750804346564731250609196846638183903982387884578266136503697493474682071", "q": "61329566248342901292543872769978950870633559608669337131139375508370458778917", "y": "11035146050035319356603071667181326439937107807515709201088015685404040978202063892648229897064130469270615066636381052131142452085234023888839560741606148281002768419540819182281284019365550459609708750759509427014369275748159616258886464283801997052408332150134151844490269051643031165719622678277666381061405843519219397144388843770110318707956593020618624814461542575834319632590650385764594793230396295178235566947356367794274375968671210737081852189350311421617066801356623920201204094622477024074187819873457371119227948971770780245662741429808612196084567496103734758919688927090038376236289215017424770469742"}, "questions": [{"answer_urls": [null, null, null, null], "answers": ["emacs", "vim", "gedit", "something else"], "choice_type": "approval", "max": 1, "min": 0, "question": "What is your favourite editor?", "result_type": "relative", "short_name": "What is your favourite editor?", "tally_type": "homomorphic"}], "short_name": "editor", "use_voter_aliases": false, "uuid": "d341fe6e-0bcf-11e2-8c73-3cd92b7981b8", "voters_hash": null, "voting_ends_at": null, "voting_starts_at": null}
\ No newline at end of file
{"num_tallied": 1, "tally": [[{"alpha": "16231187554018758103687951126977674652689607290192319102301901233153369730982889595882297018729275340990022108263238378062438440480760781494635200558451217524088777975551854978074321971979626940219129197197237387712993092004174001664944608860035693897773287318060166979911479648619040186486804754888546787753647026108252164028406196477624845031140943267596599375486401624054381110894258181756960740818707034377545357419702540575779349198115954940165943900655657262201664830803815915115293123155425616124433921225884918308359707680081083223244941741389532756326628834408522606234435599073688178162835927931318869340802", "beta": "7034175267246877538043659866394000831810644943135180686573229267601592490867583833272958746179117827484002875120289500550402901764787728267654443499229624609274150136882487955558535526512239157476190140734416233230665824073679670161274154015686921518937765367179670485240647087315409474724671396042104056012000869381268759230058338844825813299528633458411417650333694925829331207025537443461434271195459610350744944005809149441977344107023900952754665964139732290790358023812673523120373199703570040957235773438692116743580060750697278912368526379443774646197678045522710332877667012113012855577106594016334071520739"}, {"alpha": "15898639232488354667643439528795636609561668957913175509829557334027836970886227465681704135156967716579589023281731829909769953279073215323871253849908472539859798799151244816877514767403739776778722558071160091507687830368776532316641215986905958055266570822583955206520648719239982250967606977140736912063924699158023214787993037127004876642512360587284506119748466124347518490960739030301781507842672917199310134593770387643720033057372593691938788452884346357598873870321777535399138865922300716511522594669876795523902047568502466756935190650774604450978819632220952400719795922784571046726272967352290965882951", "beta": "12554293160906409572083394417836183952659300779926853229252976751649724782970270049320543193218876286245121178915011545131270094646982281865380273813223095910800357822650789373535457718140983187204489749505494823707566035974985834342646153212603087886214971624678297269834608131527454386859510756135793086041988487280884668455916750871147803644163704351305397487003545488371970225056812154121439538933756979285693860330387253433179788652826776697252605804673437789453619171566540228948527014648298337393643273323033168843910160149922168536504330003476674349223201438729360131395234386130874087951962472995822276354969"}, {"alpha": "14598899407028906701527241390334818863650978722545475543295957938352369233517012437011822674497718468554526976660645746274469236493118647720224382669800673329506723112777339285619916849350416268610538414786238573591749532619916322519439387835658766549966501303201127635350271693279220120440979743640588352307902798781122678482555114527641644187609971216120091782368503598280389260081062822949536125503642113139731909707223261842744991663235908337242331978073628414293517598226895539290028804956777840447202996381167436775515490693587234027222446721614001605889806981206911122872147239255245776971936811864787281044083", "beta": "14080030339974526247061508257532986354547794006812466501919910734933484136185866463175804374256378716016608685964009522031526373943061444536525399819409248225485041086471029218549452119756735300577586240799692853521323670006712052039932240010455116351461415224404140824312747664176013871967581825035016949198740884986723064064719612956068670728509846846433142012460566742185519552930629841766104374213499778250023986364680562641547182031457202196106484234142338118383251111039057533594043281190875164715939337920833400408206783249339326658555762142495320889147902376782289771040672008497316929336286652676562028298777"}, {"alpha": "16088713325416936095455134402224097922860724880536668884283001692929412611423408740533577756964175566757734863217128347928534013588595554029564932684525082015525956299557076920652653695701138736132535300865659065726712141443652348896130267534647766619186205416901999030575434872788460392497821017365893499294206263138771444893977921121693426082912853816418619885371231356537496424791784181729259561647845352176937107678767295850079550657368141306012838261744280190759071600730004005096841231367916265390122746250067203368219517933926875567601658851155059321322379369782128175144567000390449860268840633225300675109934", "beta": "2056392119650181302430495573826723482452196528741839501003858240161643443517744123119752704132588867395067835071518946538230381247573861962447514595365253076635393332765545973011113001408036331134664178108812312276104422495454554091344850310657056485903465607647519265740179096301913918524612535544484209625097559171030436603780022869510228684551628760155567374382801214765690425118984208209638050527608817320144991472043880018493074686692430196583107590586267222358224671887040392949981657491648120986624625805120552959276967332444370843856711745752480476488357524019173183343036490736791116893007805158549346326002"}]]}
\ No newline at end of file
{"decryption_factors": [["557921718569628523210465453415533114432234581558682432807722950476262063138318780218078692576933747933433938677192490920131127134729599337943299218722831026752960394965368780768551423724941718789188803584418531166777903076802808949315052861947289231724363141336691663881791112110357876882977553209562458677954228756798559273126772866883180312361831065629523226836954935889181198519238530664996487809939544203858912614988522534898358207091856729166816493672321490718501686857577760337879981179441256910845954255421274814724290269381822273510263727895409345933818718816945712950886180521151408885386226860359229689171", "12554293160906409572083394417836183952659300779926853229252976751649724782970270049320543193218876286245121178915011545131270094646982281865380273813223095910800357822650789373535457718140983187204489749505494823707566035974985834342646153212603087886214971624678297269834608131527454386859510756135793086041988487280884668455916750871147803644163704351305397487003545488371970225056812154121439538933756979285693860330387253433179788652826776697252605804673437789453619171566540228948527014648298337393643273323033168843910160149922168536504330003476674349223201438729360131395234386130874087951962472995822276354969", "14080030339974526247061508257532986354547794006812466501919910734933484136185866463175804374256378716016608685964009522031526373943061444536525399819409248225485041086471029218549452119756735300577586240799692853521323670006712052039932240010455116351461415224404140824312747664176013871967581825035016949198740884986723064064719612956068670728509846846433142012460566742185519552930629841766104374213499778250023986364680562641547182031457202196106484234142338118383251111039057533594043281190875164715939337920833400408206783249339326658555762142495320889147902376782289771040672008497316929336286652676562028298777", "2056392119650181302430495573826723482452196528741839501003858240161643443517744123119752704132588867395067835071518946538230381247573861962447514595365253076635393332765545973011113001408036331134664178108812312276104422495454554091344850310657056485903465607647519265740179096301913918524612535544484209625097559171030436603780022869510228684551628760155567374382801214765690425118984208209638050527608817320144991472043880018493074686692430196583107590586267222358224671887040392949981657491648120986624625805120552959276967332444370843856711745752480476488357524019173183343036490736791116893007805158549346326002"]], "decryption_proofs": [[{"challenge": "210103431342067606292683463751180547044282699293", "commitment": {"A": "900558172644318599621705627749982943953041765587405816549621576798728483806900697940185991343857884167815147751321191149992113785652948119012064771415333204648940767134576504466327763935736153011198689903910305202904350310502541210885674751189690104629249764201655014500749984604356308955615100009083235302685876466537988915761461045045640754314258349774710884331544963359167144246774982044652327561748684547852148507685319079269289453182777402869476182308504989051820038868214473621781528267887313843995581370331279354845736525350552965685599316393439741129666391331731266071952253779237118563410165552333839002078", "B": "8849702972191721177012829140282887463132268281545854919963232521702779463423761025402253236878232993571174151378919360999346502930259697643712427065855431922372051805826145944400088774085537298445488779436292491837398972736085015018957278671852808542738402697906823583062225191473333057816946351542252780253746147264993404333327617309881808686165623001574788110225271380131573827419468452315035000701442274655308697317625679897707768443641293152353764782015482170659740800438569778609669456082904121452730236278648142022038220095410594033328356224240638826584749281683533692719080489769368549927082179239783378825855"}, "response": "86102527358774054946164613573670140955370817055550656745043143488506928594103"}, {"challenge": "1123755634531363061953907079533855493130891742181", "commitment": {"A": "10148776746242500530145314392057164810283205049774265415135792731997562455773211278573941792502704620870723219650519574101413037932290498799082084936238189189664616445415300061099303301078174302808493308118590071456899611680218704072165982681285992359325351343857561631374076783829309958261637545792045102657216275079935659287478723027283124731745825581960714708617580596167418769902327730612972390175252979551453844806169349987710042781753401650632871037128452495366064030757749680469001385892141738793089639997905866537222746060225595895803004304577066162114129409623692013239135692264744162134421954795844811370820", "B": "12993535667506608369391391144064257165278076206916958976640870267405249678865111498887609894054286415479372333305392321660788999911465923427847899272450067703542831097762156223527972844475792022089202319975712332772169213407517802265294194023128933578008787316435098933689433892339311305605816818151236776390465489555775680964463953531416897986176934122228408301164966483639178891609669698016579029867444029668159577412398514427944632546860279520855153694641889324316054286708301037748317136903660677403363991642825442836526318712007283060799798832713394063197159960655048485875103132635747483821185387729907698893083"}, "response": "40511610907727624561508034722793328513082443280086884054521093866661934625418"}, {"challenge": "1154066041186580635767816694754107216399704312021", "commitment": {"A": "11226193081852679033074567460827008849964127653883613777728532499251783518346319941465820150220177852203249407610076834214484560534452866545158013631290763103564194773002799696548712448679215229704672910971203796986261413247634704361242189958681448590409188505750206385516274309530930234569770154287557907167561424897994521646525815646945922178731756915970823860731150888171421386976307015350174815688237218210581613669166864168515711348306448348305963632431612150418036169829136620733410301805897318762391292206790824894546135798486748877652877992583763717082882661250294420001281133296724520833038117172907520132252", "B": "14112425955832480227878614110187321710897379792939804235597779030689019605913741930899651738172798937832805080274759413295699168681338563266062343755055022745584055820708323766071840907647154587657804041333316077678527914797351515749468774535815195417336109702743626537288115105688413426921018220877738289796981041853142117632812161045108165180454504323292175282961194968769977675252024266987175485359236662347613629368581697992027197691575687475055242750171752001068426165640979266753871979242061354959920114570674647704334292082086425846461353680731992634519198560942704393424613978588674786470807238851879941147583"}, "response": "68836627089203833456322305379472272973879660199492191037783255835540283167566"}, {"challenge": "105828945151618414020125669844826017014947067032", "commitment": {"A": "12664313313457493941028293355546444987683760928070302741065776520190445227598378957683201613838648154510639462731208498836595143884963304489205135121977004047498737593050558776956712474612808776674729974759534963978288814939601036524224574820287871534570690703617564295865683148429715508603292074562173970829227974450418585707777432916989538900914871253760368534215304809376796858548770362597454038226188065971847060530251403788299248855076743324519454337725817983801348841937052507736609511904946978127246908391522091940496164193059598755351339885319770323690510136813131385385780125442992504721956414390419167435794", "B": "14149328989658266283185156538738641069214904338386202285562273593169328596306463275522944054477520333961348859131834040351958962873323731657407598890092104918542942354821878332992417161482986642894510615733467831332974019097710760198826703111189144562584788484140087409703933428207510252042149919453732090966773773731454586905429147178987205136844025571235646581662794971700217343879024100649490312759352793202400465497257366756672337723856389077767296898934084233250020046243404512558072841959806183788675295791578101197816793545398411688840884515043705663815427728300775066922419751439532725833233593838264437079873"}, "response": "42201196483050844569704124556090369253589913223487656599596838414876627936320"}]]}
\ No newline at end of file
{"public_key":{"g":"14887492224963187634282421537186040801304008017743492304481737382571933937568724473847106029915040150784031882206090286938661464458896494215273989547889201144857352611058572236578734319505128042602372864570426550855201448111746579871811249114781674309062693442442368697449970648232621880001709535143047913661432883287150003429802392229361583608686643243349727791976247247948618930423866180410558458272606627111270040091203073580238905303994472202930783207472394578498507764703191288249547659899997131166130259700604433891232298182348403175947450284433411265966789131024573629546048637848902243503970966798589660808533","p":"16328632084933010002384055033805457329601614771185955389739167309086214800406465799038583634953752941675645562182498120750264980492381375579367675648771293800310370964745767014243638518442553823973482995267304044326777047662957480269391322789378384619428596446446984694306187644767462460965622580087564339212631775817895958409016676398975671266179637898557687317076177218843233150695157881061257053019133078545928983562221396313169622475509818442661047018436264806901023966236718367204710755935899013750306107738002364137917426595737403871114187750804346564731250609196846638183903982387884578266136503697493474682071","q":"61329566248342901292543872769978950870633559608669337131139375508370458778917","y":"11035146050035319356603071667181326439937107807515709201088015685404040978202063892648229897064130469270615066636381052131142452085234023888839560741606148281002768419540819182281284019365550459609708750759509427014369275748159616258886464283801997052408332150134151844490269051643031165719622678277666381061405843519219397144388843770110318707956593020618624814461542575834319632590650385764594793230396295178235566947356367794274375968671210737081852189350311421617066801356623920201204094622477024074187819873457371119227948971770780245662741429808612196084567496103734758919688927090038376236289215017424770469742"},"x":"19736913957784887045176970911962160888763575987426059978300192969648710184027"}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
open Helios_datatypes_t
module type TYPES = sig
type 'a t
val read : 'a t -> Yojson.Safe.lexer_state -> Lexing.lexbuf -> 'a
val write : 'a t -> Bi_outbuf.t -> 'a -> unit
val election : election t
val private_key : private_key t
val vote : vote t
val encrypted_tally : encrypted_tally t
val partial_decryption : partial_decryption t
end
module Types : TYPES = struct
open Helios_datatypes_j
type 'a t = (Yojson.Safe.lexer_state -> Lexing.lexbuf -> 'a) * (Bi_outbuf.t -> 'a -> unit)
let read = fst
let write = snd
let election = (read_election, write_election)
let private_key = (read_private_key, write_private_key)
let vote = (read_vote, write_vote)
let encrypted_tally = (read_encrypted_tally, write_encrypted_tally)
let partial_decryption = (read_partial_decryption, write_partial_decryption)
end
let load typ fname =
let i = open_in fname in
let buf = Lexing.from_channel i in
let lex = Yojson.init_lexer ~fname () in
let result = Types.read typ lex buf in
close_in i;
result
let save typ fname x =
let o = open_out fname in
let buf = Bi_outbuf.create_channel_writer o in
Types.write typ buf x;
Bi_outbuf.flush_channel_writer buf;
close_out o
let () =
assert (Sys.command "mkdir -p _build/tests/data" = 0)
let load_and_check typ fname =
let one_thing = load typ fname in
save typ (Filename.concat "_build" fname) one_thing;
let r = Printf.ksprintf Sys.command "bash -c '\
diff -u <(json_pp < %s) <(json_pp < _build/%s)
'" fname fname
in
assert (r = 0);
one_thing
let one_election = load_and_check Types.election "tests/data/election.json"
let one_private_key = load_and_check Types.private_key "tests/data/private-key.json"
let vote_1 = load_and_check Types.vote "tests/data/vote-emacs-1.json"
let vote_2 = load_and_check Types.vote "tests/data/vote-emacs-2.json"
let encrypted_tally = load_and_check Types.encrypted_tally "tests/data/encrypted-tally.json"
let one_partial_decryption = load_and_check Types.partial_decryption "tests/data/partial-decryption.json"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment