Skip to content
Commits on Source (5)
stages:
- build_and_test_with_preinstalled_image
- build_and_test_with_debian_image
build_and_test_with_preinstalled_image:
stage: build_and_test_with_preinstalled_image
# Image swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb is based on ocaml/opam2:debian-9 with all pre-installed apt packages and opam packages that are required by Belenios
image: swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
script:
# Install required packages
# - sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
# - eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- BELENIOS_DEBUG=1 make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
- BELENIOS_DEBUG=1 make check
build_and_test_with_debian_image:
stage: build_and_test_with_debian_image
# Image ocaml/opam2:debian-9-ocaml-4.06 currently has ocaml version 4.06.1 and opam version 2.0.0 (whereas image ocaml/opam2:debian-9 currently has ocaml version 4.07.0)
image: ocaml/opam2:debian-9-ocaml-4.06
script:
# Install required packages
- sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
- eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- BELENIOS_DEBUG=1 make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
# - BELENIOS_DEBUG=1 make check
1.7.1 (2018-12-05)
==================
* Do not output spurious empty lines in records file (bugfix: voting
records and missing voters were not working)
* More explicit checklist in election validation page
* Avoid sending password/credential emails when name has not been
edited
* Avoid hidden parameters in some services that are meant to be usable
from non-web clients
1.7 (2018-11-26)
================
* Add automatic data archival/deletion policy
* Do not allow election validation if some items have not been edited
* Trustees can load their private key from a file
* Do no longer rely on Ocsipersist
* Port to OCaml 4.06.1 and Eliom 6.3.0
* Re-seed LwtRandom prng every 30 minutes
* Add a placeholder for warnings/announcements
1.6 (2018-06-13)
================
......
......@@ -20,6 +20,7 @@ The non-OCaml prerequisites are:
* [SQLite3](https://www.sqlite.org/)
* [OpenSSL](https://www.openssl.org/)
* [Wget](https://www.gnu.org/software/wget/) or [curl](http://curl.haxx.se/)
* [Zip](http://www.info-zip.org/Zip.html)
* [Unzip](http://www.info-zip.org/UnZip.html)
* [aspcud](http://www.cs.uni-potsdam.de/wv/aspcud/) (optional)
* [ncurses](http://invisible-island.net/ncurses/)
......@@ -29,7 +30,7 @@ These libraries and tools are pretty common, and might be directly part
of your operating system. On [Debian](http://www.debian.org/) and its
derivatives, they can be installed with the following command:
sudo apt install build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
sudo apt install build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates zip unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
If you are unfamiliar with OCaml or OPAM, we provide an
`opam-bootstrap.sh` shell script that creates a whole, hopefully
......@@ -40,8 +41,8 @@ variable, or it will take `~/.belenios` by default. Just run:
./opam-bootstrap.sh
On a modern desktop system, this needs approximately 10 minutes and 1
gigabyte of disk space.
On a modern desktop system, this needs approximately 11 minutes and 2
gigabytes of disk space.
If everything goes successfully, follow the given instructions to
update your shell environment, then run:
......@@ -130,25 +131,13 @@ Additionnaly, you will need LaTeX to compile the specification.
On Debian-based systems, you can install the dependencies needed to
compile the documentation with:
sudo apt-get install markdown texlive
sudo apt install markdown texlive
Once all the dependencies have been installed, the documentation can
be compiled with:
make doc
Compilation using only official Debian packages
-----------------------------------------------
At the time of writing (07 Dec 2017), you need the stable version (stretch)
of Debian (or Ubuntu) to be able to compile Belenios using only
official Debian packages. On Ubuntu, you need to enable the "Universe"
repository. Instead of using OPAM, the dependencies of Belenios can
then be installed with:
sudo apt install libatdgen-ocaml-dev libzarith-ocaml-dev libcryptokit-ocaml-dev libuuidm-ocaml-dev libcalendar-ocaml-dev libcmdliner-ocaml-dev
sudo apt install ocsigenserver eliom libcsv-ocaml-dev
Compiling on Windows using Cygwin
---------------------------------
......@@ -188,14 +177,6 @@ tool_ section above.
Troubleshooting
---------------
### OCamlDuce incompatibility
OCamlDuce is an optional transitive dependency of Belenios, but
Belenios does not use it. If OCamlDuce was installed outside of OPAM
(e.g. via your system package manager), you may face issues. You can
work around them by uninstalling OCamlDuce and restarting the
installation procedure.
### Missing sources
The instructions outlined in this document and in the
......
1.7
===
* To upgrade a web server running version 1.6, you need to delete the
Ocsipersist store (by default the `ocsidb` file referred in the
configuration file). This will archive all validated elections, and
delete all draft elections. Additionally, you should clean up the
data directory (the one referred in the `<spool>` directive in the
configuration file) by removing all temporary files (run `rm *.*`
in this directory) and private keys (`rm */private_key*.json*`).
1.1
===
......
<**/*.{ml,mli,byte,native,odoc}>: debug, annot, package(uuidm), package(atdgen), package(yojson)
<src/lib/{platform,belenios_version}.mli>: opaque
<src/platform/native/*>: package(zarith), package(cryptokit)
<src/web/*.{ml,mli,byte,native,odoc}>: thread, package(eliom.server), package(lwt.ppx), package(calendar), package(csv)
<src/tool/tool_cmdline.*>: package(zarith), package(cryptokit), package(cmdliner), use_platform-native
<src/tool/tool_js*> or <src/platform/js/*> or <src/booth/*>: package(js_of_ocaml), syntax(camlp4o), package(js_of_ocaml.syntax), package(lwt.syntax), use_platform-js
<src/tool/tool_js*> or <src/platform/js/*> or <src/booth/*>: package(js_of_ocaml-lwt), package(js_of_ocaml-ppx), package(lwt.ppx), use_platform-js
<**/*serializable_j.ml>: warn(-32)
true: warn(A-4-6-29-44-48), safe_string
true: warn(A-4-6-29-44-45-48), safe_string
belenios (1.7.1+dfsg-1) unstable; urgency=medium
* New upstream release
-- Stéphane Glondu <glondu@debian.org> Thu, 10 Jan 2019 11:32:35 +0100
belenios (1.6+dfsg-1) unstable; urgency=medium
* New upstream release
......
version=3
opts="dversionmangle=s/\+dfsg//,pgpsigurlmangle=s/$/.asc/" \
https://gforge.inria.fr/frs/?group_id=5428 .*/belenios-(.+)\.tar\.gz
version=4
opts="dversionmangle=s/\+dfsg//,repacksuffix=+dfsg,pgpmode=next" https://gforge.inria.fr/frs/?group_id=5428 .*/belenios-(.+)\.tar\.gz
opts="dversionmangle=s/\+dfsg//,repacksuffix=+dfsg,pgpmode=previous" https://gforge.inria.fr/frs/?group_id=5428 .*/belenios-(.+)\.tar\.gz\.asc
......@@ -95,7 +95,7 @@ head -n2 partial_decryptions.tmp > partial_decryptions.jsons
header "Finalize tally"
belenios-tool finalize
belenios-tool validate
header "Perform final verification"
......
......@@ -82,7 +82,7 @@ mv partial_decryptions.tmp partial_decryptions.jsons
header "Finalize tally"
belenios-tool finalize
belenios-tool validate
header "Perform final verification"
......
......@@ -30,6 +30,7 @@
<extension findlib-package="uuidm"/>
<extension findlib-package="atdgen"/>
<extension findlib-package="csv"/>
<extension findlib-package="calendar"/>
<host charset="utf-8" hostfilter="*" defaulthostname="localhost">
<!-- <redirect suburl="^$" dest="http://www.example.org"/> -->
......@@ -49,6 +50,7 @@
<default-group file="demo/groups/default.json"/>
<log file="_RUNDIR_/log/security.log"/>
<spool dir="_RUNDIR_/spool"/>
<warning file="demo/warning.html"/>
</eliom>
</host>
......
<div style="background: yellow;">This is the development version!</div>
# Documentation of the exploratory work done to enable Continuous Integration on Belenios
## How to activate Continuous Integration on the Belenios project, using Gitlab-CI
There are two possibilities:
* Keep the current main git repository on Github, and create on Gitlab a repository that would be a mirror of this repository, dedicated to Continuous Integration
* Migrate the current main git repository to Gitlab, and activate Continuous Integration on it
The two following headings detail these two procedures.
Before or after this, we have to make a `.gitlab-ci.yml` file appear in the main git repository, by accepting [the dedicated pull-request](https://github.com/glondu/belenios/pull/2) ("merge request" in the Gitlab jargon), or by creating this file directly from the Gitlab user interface.
### How to create a mirror repository on Gitlab
* Go to https://gitlab.com and log in
* On the home page ("Projects - Dashboard"), click on the "New project" button (https://gitlab.com/projects/new)
* Click on the "CI/CD for external repo" tab, then on "Repo by URL"
* In the "Git repository URL" field, write `https://gforge.inria.fr/anonscm/git/belenios/belenios.git`
* In the "Project name" field, write something like "belenios-ci"
* Click on button "Create project"
Gitlab will then regularly obtain new commits from the main repository to the mirror repository (and start a Runner that will execute continuous integration jobs if there are some).
#### How to force a refresh of the repository
If, at any moment, recent commits are not yet reflected, it is possible to ask Gitlab to immediately get new changes:
* On the project page, go to "Settings"
* then "Repositories"
* then in section "Mirorring repositories", click on button "Expand"
* in the "Mirrored repositories", click on the refresh icon.
### How to migrate the current git repository of Belenios to a Gitlab instance
* Go to the URL of your gitlab instance (or https://gitlab.com/ for the official one), and log in
* On the home page ("Projects - Dashboard"), click on the "New project" button (https://gitlab.com/projects/new)
* Follow instructions about migration of a repository
## How to execute the Gitlab-CI runner on your local machine
Install docker locally if it's not done yet.
Install `gitlab-runner` by following instructions of this page: https://docs.gitlab.com/runner/install/
Then, go to your Belenios repository folder, and run `gitlab-runner exec docker {job_name}` (where `{job_name}` is the name of the Continuous Integration job, that is the name of the key in the file `.gitlab-ci.yml` for the job that you want to run, e.g. `build`).
## How to use Docker locally to design the sequence of commands that we will write in the .gitlab-ci.yml file
Exemple:
```
$ docker run -ti ocaml/opam2:debian-9
opam@89b04864d029:~/opam-repository$ opam --version
2.0.0
opam@89b04864d029:~/opam-repository$ ocaml --version
The OCaml toplevel, version 4.06.1
opam@89b04864d029:~/opam-repository$ pwd
/home/opam/opam-repository
opam@89b04864d029:~/opam-repository$ ls
CHANGES.md CONTRIBUTING.md COPYING README.md compilers packages repo version
opam@89b04864d029:~/opam-repository$ cd
opam@89b04864d029:~$ ls
opam-repository
opam@89b04864d029:~$ git clone https://gitlab.com/swergas/belenios-ci.git
opam@89b04864d029:~$ cd belenios-ci
opam@89b04864d029:~$ sudo apt install build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev -y
opam@89b04864d029:~$ eval `grep "opam install" ./opam-bootstrap.sh`
opam@89b04864d029:~$ make all
```
## Exploration of different build strategies to optimize total execution time of Belenios' Continuous Integration pipeline
We have tried several installation methods of Belenios, to iteratively reduce total build time. The next sections explain these different installation methods, from the most classical to the most optimized.
### First strategy: Classical installation of Belenios from the official Docker image of Debian 9
To install Belenios in the classical way, that is by following as close as possible the installation procedure of the file `INSTALL.md` of the Belenios repository, we start from a Docker image of Debian 9 (the official image `debian:9` of Docker Hub).
As soon as the Gitlab-CI runner has started the container of this image, it downloads automatically the content of the Belenios repository, positionned at the commit being currently verified.
On this container, we are going to install Debian packages that are necessary to the installation of Belenios.
Then we are going to execute the `opam-bootstrap.sh` script, that installs OCaml, Opam, and the Opam packages that Belenios needs (and using version numbers that satisfy compatibility criteria of Belenios).
Lastly, we compile Belenios (via the `make all` command), and we run some early tests (`make check`, that executes a test election and verifies its coherence, as well as running the Belenios web server and executing a test checking that a given text content is present in the web page that is displayed).
The content of the `.gitlab-ci.yml` is then the following:
```
stages:
- build
build:
stage: build
image: debian:9
script:
# Install required packages
- apt-get update -qq && apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev git
# Install Opam via opam-bootstrap.sh, as recommended in INSTALL.md
- ./opam-bootstrap.sh
# Post-Opam installation procedure
- source ./env.sh
- eval `opam config env`
# Compile belenios
- make all
# Run a test of an election
- make check
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
```
The total duration of this Continuous Integration script is 24 min 07 sec on its first execution, and 23 min 29 sec on its second execution. All details are on [this page](https://gitlab.com/swergas/swergas-belenios-ci/pipelines).
### Second strategy: Installation of Belenios from a Docker image that already has OCaml and Opam installed
The classical Continuous Integration script seen un previous section downloads OCaml and Opam from their sources, compiles them and installs them. This step lasts several minutes (around 6 minutes, mainly during the construction of the "world"). It is possible to use a Docker image that has already pre-installed these tools on a linux distribution. The downloaded Docker image will then be a bit heavier than a classical linux distribution, but we save computation time.
OCaml developers maintain Docker Hub repositories `ocaml/opam` and `ocaml/opam2` (respectively for Opam 1.x.x and Opam 2.x.x), that contain images of several different linux distributions (including Debian 9), on which som versions of OCaml and Opam have been installed.
The classical installtion procedure of Belenios currently uses OCaml 4.02.3 and Opam 1.2.2. I have not been able to make work the installation of the Opam packages with a Docker image that has this version of Opam installed, because of an dependencies resolution error. But, with an image that has Opam 2.0.0 installed, there was no problem, so I have chosen this version (image `ocaml/opam2:debian-9`).
Regarding script `.gitlab-ci.yml`, the difference with the classical installation procedure (that uses a Debian 9 image), resides in the fact that instead of executing the whole `opam-bootstrap.sh` script, we execute only the part of it that installs Opam packages: ```eval `grep "opam install" ./opam-bootstrap.sh` ```
The content of the `.gitlab-ci.yml` is then the following:
```
stages:
- build
build:
stage: build
# Image ocaml/opam2:debian-9 currently has ocaml version 4.06.1 and opam version 2.0.0
image: ocaml/opam2:debian-9
script:
# Install required packages
- sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
- eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
- make check
```
The Gitalb-CI runner takes around 2 minutes to get the `ocaml/opam2:debian-9` image on the first time (and takes 1 min 50 sec on the second time, so there is probably no caching of Docker images by the Gitlab-CI runner from one execution to the other).
Then, as previously, it clones the git repository.
At 2 min 42 sec, it begins installing apt-get packages.
At 3 min 34 sec, all Opam packages have been downloaded, and some of them are already installed.
At 15 min 47 sec, all Opam packages are installed (their installation order varies from one execution to the other, but we find globally the same ones at the end of the list). During the second exeuction, this step is finised at 15 min 52 sec. We remark that packages `ocsigenserver` and `eliom` are the last 2 ones to install and are the ones that thake the longest time to install. The next step is called `make all`.
At 16 min 28 sec, the runner starts executing the `make archive` step.
Total duration of the first launch (without the `make check` step): 16 min 57 sec.
Total duration of the second launch (with the `make check` step): 17 min 31 sec.
Conclusion: Moving from the `debian:9` image to the`ocaml/opam2:debian-9` image, we moved from a total execution time of 23 min 30 sec to 17 min 31 sec, that is a diminution of 6 minutes (diminution of 25.53 %).
### Third strategy: Using a custom Docker image, that has also pre-installed the apt-get packages and the Opam packages
A possibility to reduce again the total execution time of the Continuous Integration script, consists in creating a Docker image based upon `ocaml/opam2:debian-9`, that has already executed the steps of installing the `apt-get` packages, as well as the Opam packages, which would make a difference of approximately 13 min 05 sec (estimated by this calculation: 15 min 47 sec - 2 min 42 sec).
Note that this solution has a non negligeable drawback, that is that we have to re-create a new Docker image and reference it in the `.gitlab-ci.yml` file, every time we change the apt depencencies and/or the Opam dependencies in the code (adding dependencies, removing dependencies, changing version numbers of some dependencies, changing version numbers of OCaml or Opam).
So I have created the [swergas/beleniosbase](https://hub.docker.com/r/swergas/beleniosbase/) repository on Docker Hub, where I have placed such an image, using as image tag the checksum of the file `opam-bootstrap.sh`. This way, when the code of Belenios changes the version numbers of Ocaml or Opam that it requires, or that the list of its required Opam packages changes, we will be able to make correspond to it a unique Docker image name.
Here is the procedure to create such a Docker image and publish it on Docker Hub:
```
$ sha256sum ./opam-bootstrap.sh
efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb ./opam-bootstrap.sh
$ docker container run -ti ocaml/opam2:debian-9 /bin/bash
$ sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
$ opam install --yes atdgen zarith cryptokit uuidm calendar cmdliner sqlite3 eliom=6.3.0 csv
$ exit
$ docker container ls -a
$ docker container commit <CONTAINER_ID>
# Exemple : docker container commit fe173ea3829c
$ docker image ls
$ docker image tag <IMAGE_ID> <THE_IMAGE_NAME_YOU_WANT>
# Exemple : docker image tag 04bf023e658e beleniosbasewithopamdependencies2
$ docker commit <CONTAINER_ID> <YOUR_DOCKERHUB_USERNAME>/<YOUR_DOCKERHUB_REPOSITORY_NAME>:<TAG_NAME>
# Exemple : docker commit fe173ea3829c swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
# Le résultat de la commande est de la forme 'sha256:c3b4dd5f41f071409d6f45f069f5bef7b4eb236d2bfbb457c0be89ae1f8a4139'
$ docker push <hub-user>/<repo-name>:<tag>
# Exemple : docker push swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
# Lorsque ce n'est pas la première fois qu'on envoie l'image, le résultat de cette commande est :
# The push refers to repository [docker.io/swergas/beleniosbase]
# e496191c6f91: Pushed
# 932b4034c234: Layer already exists
# aa956106affb: Layer already exists
# 7fc2f0c53c72: Layer already exists
# 555b3e37ead3: Layer already exists
# b9190cafe4f2: Layer already exists
# 0e6751af6de3: Layer already exists
# d04afccd7138: Layer already exists
# b28ef0b6fef8: Layer already exists
# efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb: digest: sha256:f508797f44a37314b96120f46537fcf426995490f6f4f318db2e2662b45cb860 size: 2221
```
We can then reference this Docker image name in our `.gitlab-ci.yml` file and comment or remove the installation steps that are not necessary anymore:
```
stages:
- build
build:
stage: build
# Image ocaml/opam2:debian-9 currently has ocaml version 4.06.1 and opam version 2.0.0
# image: ocaml/opam2:debian-9
# Image swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb is based on ocaml/opam2:debian-9 with all pre-installed apt packages and opam packages that are required by Belenios
image: swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb
script:
# Install required packages
# - sudo apt-get update -qq && sudo apt-get install -y -qq build-essential libgmp-dev libpcre3-dev pkg-config m4 libssl-dev libsqlite3-dev wget ca-certificates unzip aspcud libncurses-dev uuid-runtime zlib1g-dev
# Install the same Opam packages that opam-bootstrap.sh installs
# - eval `grep "opam install" ./opam-bootstrap.sh`
# Compile belenios
- make all
# Create a bundled version of belenios (this produces a belenios.tar.gz file, which is needed by the web server)
- make archive
# Start belenios web server
- ./demo/run-server.sh &
# Access the localhost web page, print page output for debug purposes, and check validity of page output
- first_access_index_page_output=$(wget --retry-connrefused --no-check-certificate -T 30 http://localhost:8001 -O-)
- echo $first_access_index_page_output
- if [ "$(echo \"$first_access_index_page_output\" | grep '>Belenios</a>' | wc -l)" != "1" ]; then echo "[First page access] First page access does not show a single '>Belenios</a>' text, but it should" && exit 1; else echo "[First page access] First page access shows a single '>Belenios</a>' text, as expected"; fi
# Run a test of an election
- make check
```
At 2 min 50 sec, the runner has finished downloading the Docker image and starts cloning the repository.
At 3 min 53 sec, it has begun running the `make all` command.
At 4 min 24 sec, it has finished running the `make all` command and has begun running the `make check` command.
Total execution time: 11 min 25 sec.
Moving from image `ocaml/opam2:debian-9` to `swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb` changed total execution time from 7 min 31 sec à 11 min 25 sec, that is a diminution of 6 min 06 sec (diminution of 34.82 %).
### Using urandom instead of random
When trying to run the CI jobs, [we noticed that sometimes jobs were finishing in a timeout error](https://github.com/glondu/belenios/pull/2#issuecomment-422412068). By default, `belenios-tool` uses secure random (`/dev/random`), which may exhaust the entropy pool when it is run many times (which is the case with `make check`). The `BELENIOS_DEBUG` environment variable at build time triggers a different code path that uses `/dev/urandom` instead.
So we decided to use urandom instead of random in our Continuous Integration scripts. This makes CI jobs more reliable and reduces total execution time even more (13 minutes 13 seconds on a `ocaml/opam2:debian-9-ocaml-4.06` image, and 5 minutes 33 seconds with the `swergas/beleniosbase:efa5df3049f736dd34eb8289da730dd709eb99939f6511fa93ae0080a61ce4fb` image).
### Using a fixed version of Ocaml
We [noticed](https://github.com/glondu/belenios/pull/2#issuecomment-423271172) that Docker image `ocaml/opam2:debian-9` regularly gets re-built using a different version of OCaml (it is a kind of `latest` tag for the debian distribution). Some of these builds containe a version of OCaml that breaks the build script. So, in order to use an image that always has a version of OCaml that we know works, we stabilize the version number used, by using an image name that mentions precisely the OCaml version used, for example `ocaml/opam2:debian-9-ocaml-4.06` instead of just `ocaml/opam2:debian-9`.
### Recap of total execution durations
| Docker image | Using fast random | Total duration |
| ------------------------------- |:-----------------:|:---------------:|
| debian:9 | no | 23 min 29 sec |
| ocaml/opam2:debian-9-ocaml-4.06 | no | 17 min 31 sec |
| swergas/-checksum- | no | 11 min 25 sec |
| ocaml/opam2:debian-9-ocaml-4.06 | yes | 13 min 13 sec |
| swergas/-checksum- | yes | 05 min 33 sec |
......@@ -7,7 +7,7 @@
\usepackage{bbm}
\usepackage{hyperref}
\newcommand{\version}{1.5}
\newcommand{\version}{1.6}
\newcommand{\F}{\mathbbm{F}}
\newcommand{\G}{\mathbbm{G}}
......@@ -195,7 +195,10 @@ $\textsf{field}(o)$ to access the field \textsf{field} of $o$.
\begin{itemize}
\item $\jstring$: JSON string
\item $\uuid$: UUID (see RFC 4122), encoded as a JSON string
\item $\uuid$: UUID (either as defined in RFC 4122, or a string of
Base58 characters\footnote{Base58 characters are:
\texttt{123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz}}
of size at least 14), encoded as a JSON string
\item $\I$: small integer, encoded as a JSON number
\item $\B$: boolean, encoded as a JSON boolean
\item $\N$, $\Z_q$, $\G$: big integer, written in base 10 and encoded as a
......@@ -576,7 +579,8 @@ PBKDF2 (RFC 2898) with:
\begin{itemize}
\item $c$ as password;
\item HMAC-SHA256 (RFC 2104, FIPS PUB 180-2) as pseudorandom function;
\item the $\uuid$ (interpreted as a 16-byte array) of the election as
\item the $\uuid$ (either interpreted as a 16-byte array in the RFC
4122 case, or directly itself in the Base58 case) of the election as
salt;
\item $1000$ iterations
\end{itemize}
......
......@@ -104,7 +104,7 @@ several authentication mechanisms.
2. Concatenate the `partial_decryption.json` received from each
trustee into a `partial_decryptions.jsons`, in the same order as in
`public_keys.jsons`.
3. Run `belenios-tool finalize`. It will create
3. Run `belenios-tool validate`. It will create
`result.json`. Publish this file, along with the files listed in
the first step above. The whole set will enable universal
verifiability.
......
......@@ -41,7 +41,7 @@ Then, the administrator must:
credential authority;
* (optionally) edit trustees. For good security there should be at
least two trustees; a link is generated for each trustee;
* finalize the election.
* validate the election.
Each "link" above must be sent by the administrator to their intended
recipient. Each link leads to an interface that will help its
......@@ -56,15 +56,15 @@ into:
### Election life cycle
An election starts by being in preparation (or "setup mode"), then
becomes finalized. Then, it is immediately opened and can be closed
An election starts by being in preparation (or "draft mode"), then
becomes validated. Then, it is immediately opened and can be closed
and re-opened at will. When it is closed, the election administrator
can initiate the tallying process. The encrypted tally is then
computed and published. After each trustee has computed his/her share
of the decryption, the administrator triggers the release of the
result.
At any moment, a finalized election can be archived. This releases
At any moment, a validated election can be archived. This releases
some resources on the server and makes the election read-only. In
particular, it is no longer possible to vote in or to tally an
archived election. Be careful, this operation is not revertible.
......
......@@ -101,6 +101,9 @@ let () = dispatch & function
(* the following avoids an ocamlfind warning, it should be built-in *)
flag ["doc"; "thread"] (A"-thread");
(* there seems to be no built-in tag for this... *)
flag ["compile"; "interf"] (A"-no-keep-locs");
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"]
......
......@@ -86,13 +86,13 @@ echo "=-=-= Initialization of OPAM root =-=-="
echo
opam init --no-setup
eval `opam config env`
opam switch 4.02.3
opam switch 4.06.1
eval `opam config env`
echo
echo "=-=-= Installation of Belenios build-dependencies =-=-="
echo
opam install --yes atdgen=1.12.0 zarith cryptokit uuidm calendar cmdliner sqlite3 eliom=4.2.0 csv
opam install --yes atdgen zarith cryptokit uuidm calendar cmdliner sqlite3 ssl=0.5.5 eliom=6.3.0 csv
echo
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
......
......@@ -25,7 +25,7 @@ open Serializable_j
open Signatures
open Common
let document = Dom_html.window##document
let document = Dom_html.window##.document
let withElementById x f =
Js.Opt.iter (document##getElementById (Js.string x)) f
......@@ -33,7 +33,7 @@ let withElementById x f =
let getHtmlById x =
let r = ref x in
withElementById x (fun x ->
Js.Opt.iter (x##textContent) (fun x -> r := Js.to_string x)
Js.Opt.iter (x##.textContent) (fun x -> r := Js.to_string x)
); !r
let alert s : unit =
......@@ -55,14 +55,14 @@ let runHandler handler () =
let installHandler id handler =
let f _ = runHandler handler () in
withElementById id (fun e -> e##onclick <- Dom_html.handler f)
withElementById id (fun e -> e##.onclick := Dom_html.handler f)
let getTextarea id =
let res = ref None in
withElementById id (fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.textarea e)
(fun x -> res := Some (Js.to_string (x##value)))
(fun x -> res := Some (Js.to_string (x##.value)))
);
match !res with
| None -> raise Not_found
......@@ -72,7 +72,7 @@ let setTextarea id z =
withElementById id (fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.textarea e)
(fun x -> x##value <- Js.string z)
(fun x -> x##.value := Js.string z)
)
let setNodeById id x =
......@@ -82,7 +82,7 @@ let setNodeById id x =
)
let setDisplayById id x =
withElementById id (fun e -> e##style##display <- Js.string x)
withElementById id (fun e -> e##.style##.display := Js.string x)
let prng = lazy (pseudo_rng (random_string secure_rng 16))
......@@ -95,7 +95,7 @@ module LwtJsRandom = struct
let random q =
let size = Z.bit_length q / 8 + 1 in
fun () ->
lwt () = Lwt_js.yield () in
let%lwt () = Lwt_js.yield () in
let r = random_string (Lazy.force prng) size in
Lwt.return Z.(of_bits r mod q)
end
......@@ -106,21 +106,21 @@ let encryptBallot params cred plaintext () =
let module E = Election.Make (P) (LwtJsRandom) in
let module CD = Credential.MakeDerive (G) in
let sk = CD.derive P.election.e_params.e_uuid cred in
lwt randomness = E.make_randomness () () in
lwt b = E.create_ballot ~sk randomness plaintext () in
let%lwt randomness = E.make_randomness () () in
let%lwt b = E.create_ballot ~sk randomness plaintext () in
let s = string_of_ballot G.write b in
setTextarea "ballot" s;
setNodeById "ballot_tracker" (sha256_b64 s);
setDisplayById "encrypting_div" "none";
setDisplayById "ballot_div" "block";
Dom_html.window##onbeforeunload <- Dom_html.no_handler;
Dom_html.window##.onbeforeunload := Dom_html.no_handler;
Lwt.return ()
let progress_step n =
let old_ = Printf.sprintf "progress%d" (n-1) in
let new_ = Printf.sprintf "progress%d" n in
withElementById old_ (fun e -> e##setAttribute (Js.string "style", Js.string ""));
withElementById new_ (fun e -> e##setAttribute (Js.string "style", Js.string "font-weight: bold;"))
withElementById old_ (fun e -> e##setAttribute (Js.string "style") (Js.string ""));
withElementById new_ (fun e -> e##setAttribute (Js.string "style") (Js.string "font-weight: bold;"))
let rec createQuestionNode sk params question_div num_questions i prev (q, answers) next =
(* Create div element for the current question. [i] and [(q,
......@@ -159,13 +159,13 @@ let rec createQuestionNode sk params question_div num_questions i prev (q, answe
| Some x -> x
| None -> failwith "error while casting checkbox"
in
if answers.(i) > 0 then cb##checked <- Js.bool true;
checkbox##setAttribute (Js.string "type", Js.string "checkbox");
checkbox##setAttribute (Js.string "style", Js.string "cursor: pointer;");
if answers.(i) > 0 then cb##.checked := Js.bool true;
checkbox##setAttribute (Js.string "type") (Js.string "checkbox");
checkbox##setAttribute (Js.string "style") (Js.string "cursor: pointer;");
Dom.appendChild div checkbox;
let t = document##createTextNode (Js.string a) in
checkbox##onclick <- Dom_html.handler (fun _ ->
answers.(i) <- if Js.to_bool cb##checked then 1 else 0;
checkbox##.onclick := Dom_html.handler (fun _ ->
answers.(i) <- if Js.to_bool cb##.checked then 1 else 0;
Js._true
);
Dom.appendChild div t;
......@@ -215,7 +215,7 @@ let rec createQuestionNode sk params question_div num_questions i prev (q, answe
let () =
(* previous button *)
let btns = document##createElement (Js.string "div") in
btns##setAttribute (Js.string "style", Js.string "text-align: center;");
btns##setAttribute (Js.string "style") (Js.string "text-align: center;");
let () =
match prev with
| [] ->
......@@ -224,7 +224,7 @@ let rec createQuestionNode sk params question_div num_questions i prev (q, answe
| r :: prev ->
let b = document##createElement (Js.string "button") in
let t = document##createTextNode (Js.string @@ getHtmlById "str_previous") in
b##onclick <- Dom_html.handler (fun _ ->
b##.onclick := Dom_html.handler (fun _ ->
if check_constraints () then (
let ndiv = createQuestionNode sk params
question_div num_questions (i - 1) prev r ((q, answers) :: next)
......@@ -243,13 +243,13 @@ let rec createQuestionNode sk params question_div num_questions i prev (q, answe
(* last question, the button leads to encryption page *)
let b = document##createElement (Js.string "button") in
let t = document##createTextNode (Js.string @@ getHtmlById "str_next") in
b##onclick <- Dom_html.handler (fun _ ->
b##.onclick := Dom_html.handler (fun _ ->
if check_constraints () then (
let all = (q, answers) :: prev in
let all_answers = List.rev_map snd all |> Array.of_list in
let all_questions = List.rev_map fst all |> Array.of_list in
setTextarea "choices" (string_of_plaintext all_answers);
question_div##style##display <- Js.string "none";
question_div##.style##.display := Js.string "none";
withElementById "pretty_choices" (fun e ->
Array.iteri (fun i a ->
let q = all_questions.(i) in
......@@ -290,7 +290,7 @@ let rec createQuestionNode sk params question_div num_questions i prev (q, answe
| r :: next ->
let b = document##createElement (Js.string "button") in
let t = document##createTextNode (Js.string @@ getHtmlById "str_next") in
b##onclick <- Dom_html.handler (fun _ ->
b##.onclick := Dom_html.handler (fun _ ->
if check_constraints () then (
let ndiv = createQuestionNode sk params
question_div num_questions (i + 1) ((q, answers) :: prev) r next
......@@ -322,14 +322,14 @@ let addQuestions sk params qs =
let createStartButton params intro_div qs =
let b = document##createElement (Js.string "button") in
b##setAttribute (Js.string "style", Js.string "font-size:20px;");
b##setAttribute (Js.string "style") (Js.string "font-size:20px;");
let t = document##createTextNode (Js.string (getHtmlById "str_here")) in
b##onclick <- Dom_html.handler (fun _ ->
b##.onclick := Dom_html.handler (fun _ ->
(match prompt (getHtmlById "enter_cred") with
| Some cred when Credential.check cred ->
intro_div##style##display <- Js.string "none";
intro_div##.style##.display := Js.string "none";
setDisplayById "question_div" "block";
Dom_html.window##onbeforeunload <- Dom_html.handler (fun _ ->
Dom_html.window##.onbeforeunload := Dom_html.handler (fun _ ->
Js._false
);
progress_step 2;
......@@ -378,14 +378,9 @@ let get_url x =
with Not_found -> None
let load_url url =
withElementById "ballot_form" (fun e ->
Js.Opt.iter
(Dom_html.CoerceTo.form e)
(fun e -> e##action <- Js.string (url ^ "cast"))
);
let open XmlHttpRequest in
let open Lwt_xmlHttpRequest in
Lwt.async (fun () ->
lwt raw = get (url ^ "election.json") in
let%lwt raw = get (url ^ "election.json") in
let () = setTextarea "election_params" raw.content in
Lwt.return (runHandler loadElection ())
)
......@@ -393,7 +388,7 @@ let load_url url =
let load_url_handler _ =
let url = getTextarea "url" in
let encoded = Url.encode_arguments ["url", url] in
Dom_html.window##location##hash <- Js.string encoded;
Dom_html.window##.location##.hash := Js.string encoded;
load_url url;
Js._false
......@@ -409,16 +404,16 @@ let load_params_handler _ =
let onload_handler _ =
let () =
withElementById "load_url"
(fun e -> e##onclick <- Dom_html.handler load_url_handler);
(fun e -> e##.onclick := Dom_html.handler load_url_handler);
withElementById "load_params"
(fun e -> e##onclick <- Dom_html.handler load_params_handler);
(fun e -> e##.onclick := Dom_html.handler load_params_handler);
in
let () =
match get_url (Js.to_string Dom_html.window##location##hash) with
match get_url (Js.to_string Dom_html.window##.location##.hash) with
| None ->
setDisplayById "wait_div" "none";
setDisplayById "election_loader" "block";
| Some url -> load_url url
in Js._false
let () = Dom_html.window##onload <- Dom_html.handler onload_handler
let () = Dom_html.window##.onload := Dom_html.handler onload_handler
......@@ -144,14 +144,32 @@ module String = struct
xn >= sn && String.sub x 0 sn = s
end
let rec list_join sep = function
module List = struct
include List
let rec join sep = function
| [] -> []
| [x] -> [x]
| x :: xs -> x :: sep :: list_join sep xs
| x :: xs -> x :: sep :: join sep xs
let rec filter_map f = function
| [] -> []
| x :: xs ->
let ys = filter_map f xs in
match f x with
| None -> ys
| Some y -> y :: ys
end
module Option = struct
let get x default_value = match x with
| None -> default_value
| Some x -> x
let option_map f = function
let map f = function
| Some x -> Some (f x)
| None -> None
end
let save_to filename writer x =
let oc = open_out filename in
......