Glove is a pseudo-anonymous service for Polkadot OpenGov enabling voters to partake in referenda without revealing their voting preferences. The Glove service leverages the confidentiality of secure enclaves to mix votes in a secure and verifiable manner. This reference implementation uses AWS Nitro Enclaves.
You can read more about Glove and how it achieves these goals in the technical design.
Table of Contents
You will need an x86-64 machine with docker installed to fully build Glove:
./build.sh
This will build the service, enclave and client CLI binaries. The enclave image measurement, PCR0
, will be printed at
the end:
Enclave Image successfully created.
{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "609e2ce86435160b1aba2f1995bdab26c8ce0f3db0d81fb435166e22b6d95261cbee8ed046469b009d1048e614227cde",
...
}
}
If you're using a Glove service and want to confirm the on-chain vote was mixed from a genuine Glove enclave, run the following command:
target/release/client --glove-url=<GLOVE SERVICE URL> verify-vote --account=<GLOVE CLIENT ACCOUNT> --poll-index=<POLL INDEX> --enclave-measurement=<EXPECTED ENCLAVE MEASUREMENT>
Note
You can build just the client with ./build.sh client
.
This will perform the following checks:
- The Glove proxy account has voted on the given poll
- The on-chain vote comes accompanied with a valid signed Glove proof
- The Glove proof was produced by a genuine AWS Nitro enclave
- The proof mirrors the on-chain vote for the account
This is an example output of a successful verification:
Nonce was not provided so cannot check if most recent vote request was used by Glove proxy
Vote mixed by VERIFIED Glove enclave: Nay using 0.979 ROC with conviction None
Note
The warning about the nonce can be resolved by providing the nonce of the vote request with the --nonce
flag.
--enclave-measurement
is the expected audited Glove enclave identity. If this is not known then run the command
without it, and if the client determines the vote was mixed by a potential Glove enclave it will print out
instructions on how to audit and verify the enclave code:
Nonce was not provided so cannot check if most recent vote request was used by Glove proxy
Vote mixed by POSSIBLE Glove enclave: Nay using 0.979 ROC with conviction None
To verify this is a Glove enclave, first audit the code:
git clone --depth 1 --branch v0.0.12 https://github.com/projectglove/glove-monorepo/
And then check 'PCR0' output is '0x609e2ce86435160b1aba2f1995bdab26c8ce0f3db0d81fb435166e22b6d95261cbee8ed046469b009d1048e614227cde' by running:
./build.sh
The --branch
version that's printed is specific to the Glove proof for that vote (and not necessarily the
current version of the Glove client or service), and building the enclave for that version should produce the
expected PCR0
measurement.
Note
The client can also be used to submit vote requests. Run with --help
to see full options.
These are the rules when comes to vote requests and mixing:
- The mixing of the vote requests always occurs inside the enclave, the code of which can be found here.
- The mixing will be delayed as late as possible in the poll's timeline (details below). This is to prevent leakage of information of the private vote requests, something which can happen if there are multiple mixes.
- This means there will only be one mixing event, and thus vote requests and remove vote requests after the mix will be rejected.
- If the vote request returns with a success the service will include it in the mixing, even if it is the only request for that poll.
- The assigned netted vote balance will never be more than the balance in the original vote request.
- If one on the rare event all of the vote requests net to a balance of zero (i.e. neither aye or nay) then the Glove vote will be abstain with a balance of one.
- If at the time of mixing an account has insufficient funds to cover their assigned vote balance, their vote will be removed and mix attempted again.
- If a participant also votes on the same poll outside of Glove, their vote request will be removed. If the service has already mixed the votes then a re-mix will be attempted immediately. This is the only scenario where a poll will be mixed more than once.
The Glove service will initiate a mxing of the vote requests for a poll when one of the following conditions are met:
- The poll reaches near the end of its decision period (but still enough time to mix and submit on-chain). There are
two scenarios:
- The poll hasn't entered confirmation and is on its way to be rejected. The mixed votes will be submitted, even if they are "nay" and only confirming the poll's rejection. This is necessary to show the Glove proxy is not withholding votes.
- The poll is in confirmation. Even though the confirmation period will extend beyond the poll's decision period, it's possible for a non-Glove voter to take it out of confirmation, and thus cause it to be rejected immediately. Thus the Glove service risks not being able to mix if it waits until the end of the confirmation period.
- The poll reaches near the end of its confirmation period and it's still within its decision period. Since the poll is on course to be accepted if the confirmation period elapses, the Glove service will need to mix before then.
Warning
Testing and development can be difficult with this behaviour as decision periods can last days. To alleviate this,
the --regular-mix
flag can turn this off and mix votes on a regular basis. However, this MUST NOT be enabled in
production as it will leak private information of the vote requests.
If you want to run your own Glove service, you will need to have a compatible AWS EC2 instance with AWS Nitro Enclaves enabled. You can follow the instructions here to provision the correct EC2 instance. Make sure to use x86-64, with the Nitro Enclaves option enabled.
Then install the Nitro Enclaves CLI. Make sure to allocate at least 512 MiB for the enclave.
You will also need to create a DynamoDB table for the service. The table must have a sort key, and both partition and sort keys must be strings. Make sure to attach an IAM role to the EC2 instance which gives it write access to the table.
Make sure the service
binary and the glove.eif
file are in the same directory. If you built using build.sh
they
will both be in target/release
:
target/release/service --address=<LISTEN> --proxy-secret-phrase=<SECRET PHRASE> --node-endpoint=<URL> --subscan-api-key=<API KEY> dynamodb --table-name=<GLOVE TABLE>
The service makes use of Subscan and it is recommended an API key be provided, though not required.
Full documenation of the arguments, along with other flags, can be found with --help
.
You can check the enclave is running with:
nitro-cli describe-enclaves
If the enclave fails to start or you want to view its logs, start the service with --enclave-mode=debug
which will
start the enclave in debug mode and output to the console.
Warning
Debug mode is not secure and will be reflected in the enclave's remote attestation and any Glove proofs created. Do not enable this in production.
The Glove service exposes a REST API for submitting votes and interacting with it.
Get information about the Glove service, including the enclave. This can also act as a health check.
None
A JSON object with the following fields:
The Glove proxy account address. Users will need to first assign this account as their governance proxy before they can submit votes.
The substrate-based network the Glove service is connected to. This is a SS58 unique identifier.
The attestation bundle of the enclave the service is using. This is a hex string representing the
AttestationBundle
struct in
SCALE encoding.
The attestation bundle is primarily used in Glove proofs when the enclave submits its mixed votes on-chain. It's available here for clients to verify the enclave's identity before submitting any votes.
The version of the Glove service.
{
"proxy_account": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"network_name": "rococo",
"attestation_bundle": "0x6408de7737c59c238890533af25896a2c20608d8b380bb01029acb3927...",
"version": "0.0.8"
}
Submit a signed vote request to be included in the Glove mixing process.
Multiple votes can be submitted for the same poll, but it's up to the discretion of the Glove service to accept them. If they are accepted they will replace the previous vote for that poll.
A JSON object with the following fields:
SCALE-encoded VoteRequest
as a
hex string.
SCALE-encoded
MultiSignature
as a hex string. The
signature is of the VoteRequest
in SCALE-encoded bytes, i.e. the request
field without the hex-encoding, signed by
VoteRequest.account
.
This example shows how to create a signed vote request JSON body using the Polkadot JS API. The request is made by the Bob dev account on the Rococo network for a vote of aye, on poll 185, using 2.23 ROC at 2x conviction.
If the vote request was successfully received and accepted by the service then an empty response with 200 OK
status
code is returned. This does not mean, however, the vote was mixed and submitted on-chain; just that the Glove service
will do so at the appropriate time.
If request body is invalid then a 422 Unprocessable Entity
is returned. If there was something wrong with the
request itself then a 400 Bad Request
is returned with a JSON object containing the error type (error
) and
description (description
). 429 Too Many Requests
can also be returned and the request should be retried later.
Submit a signed remove vote request for removing a previously submitted vote.
A JSON object with the following fields:
SCALE-encoded RemoveVoteRequest
as a hex string.
SCALE-encoded
MultiSignature
as a hex string. The
signature is of the RemoveVoteRequest
in SCALE-encoded bytes, i.e. the request
field without the hex-encoding,
signed by RemoveVoteRequest.account
,
An empty response with 200 OK
status code is returned if the previous vote was successfully removed or if there was
no matching vote.
If request body is invalid then a 422 Unprocessable Entity
is returned. If there was something wrong with the
request itself then a 400 Bad Request
is returned with a JSON object containing the error type (error
) and
description (description
). 429 Too Many Requests
can also be returned and the request should be retried later.
Get Glove-specific information about poll with index poll_index
.
None
A JSON object with the following fields:
Estimated time the Glove service will mix vote requests and submit them on-chain. If the service hasn't received requests for this poll then this is the time the service would mix if it had. This field is only available for polls which have reached the decision phase. The mixing time isn't fixed and may change as the poll progresses. It is thus recommended to occasionally poll this end-point.
The time is given in terms of both block number and UNIX timestamp seconds; the value is an object with fields
block_number
and timestamp
.
If the poll is not active then 400 Bad Request
is returned.
{
"mixing_time": {
"block_number": 22508946,
"timestamp": 1726170438
}
}
If building on MacOS, then use cargo
directly rather than the ./build.sh
script. Only mock mode will be available.
You first need the subxt-cli
tool installed:
cargo install subxt-cli
Then run this in the home directory of this project:
subxt metadata --url="wss://rpc.polkadot.io:443" -f bytes > assets/polkadot-metadata.scale
This repo also has example configuration as code scripts for deployment of the Glove service, located in the devops directory. Cloud infrastructure is handled by Terraform/OpenTofu and VM configuration by Ansible.
The scripts in the terraform subfolder are responsible for deployment of:
- VM with Enclave,
- Application Load Balancer (ALB),
- DNS entries,
- SSL certificate for a test system,
- DynamoDB table,
- and matching Security Groups (SG) and IAM roles.
Please note, that in the test TLS traffic terminates on the load balancer, not VM.
In the Ansible folder there is the glove role and the matching playbook with an inventory file for the test deployment. The role gets the latest binaries from the github, release page, sets systemd service for glove API host and prepare Nitro enclave.
The Ansible playbook and the role are used in the release GHA. Whenever a new git tag is pushed, the action builds the binaries, and then uses Ansible to update the test deployment.
Important
The git tag must begin with v
and match the project version in Cargo.toml. This is important to
ensure users download the correct version of the enclave code when verifying Glove proofs.
To check the status of the service run:
systemctl status glove
Glove service logs are picked up by SystemD and can be accessed with:
journalctl -u glove
or to follow the flow of logs:
journalctl -fu glove
or to get extra information on latest logs:
journalctl -xeu glove