Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker images and static binaries #2

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 0 additions & 111 deletions .circleci/config.yml

This file was deleted.

4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target/
keypair
.env
solidity/cache
56 changes: 56 additions & 0 deletions .github/workflows/packages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Packages

on:
push:
branches:
- 'master'
tags:
- '*.*.*'
pull_request:
branches:
- 'master'

jobs:
build:
name: Build and push docker images to ghcr.io
runs-on: ubuntu-latest
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE_BASE: ${{ github.repository_owner }}
outputs:
operator: ${{ steps.meta-tezos-operator.outputs.tags }}
steps:
- name: Check out the repo
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Log in to the registry
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set image tags & labels
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_BASE }}/dkg-cli

- name: Image build & push
uses: docker/build-push-action@v2
with:
context: .
platforms: linux/amd64,linux/arm64,
file: crates/dkg-cli/Dockerfile
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
33 changes: 33 additions & 0 deletions .github/workflows/releases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Releases

on:
push:
tags:
- '*.*'

jobs:
build:
name: Build and publish static binaries
runs-on: ubuntu-latest

strategy:
matrix:
target: [x86_64-unknown-linux-gnu]

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true

- name: Build
run: RUSTFLAGS="-C target-feature=-crt-static" NO_SOLC_BUILD=1 cargo build --release --target=${{ matrix.target }}

- name: Release
uses: softprops/action-gh-release@v2
with:
files: target/release/dkg-cli
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Tests

on:
pull_request:
push:
branches:
- 'master'

jobs:
test:
name: Check formatting and run unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy

- name: Check formatting
run: cargo fmt --check

- name: Run tests
run: NO_SOLC_BUILD=1 cargo test
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ react-native

solidity/cache
solidity/build
solidity/artifacts
*.swp
*.bin
.DS_Store
*.json
crates/dkg-cli/src/dkg_contract.rs

keypair
dkg-output
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ run:

start:
./target/release/dkg-cli start -n $(NODE_URL) -p $(PRIVATE_KEY) -c $(CONTRACT_ADDRESS)

image:
docker build -t dkg-cli -f crates/dkg-cli/Dockerfile .
56 changes: 25 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,44 @@
<h1 align="center">Threshold BLS Signatures and DKG</h1>
# DKG tooling

Fork of the Celo [project](https://github.com/celo-org/celo-threshold-bls-rs).
This is a fork of the Celo's [threshold-bls](https://github.com/celo-org/celo-threshold-bls-rs) project.

## Overview

This crate provides libraries and command line interfaces for producing threshold BLS signatures. The signatures can also be [blind](https://en.wikipedia.org/wiki/Blind_signature) in order to preserve the privacy of the user asking for a signature from another set of parties.
This project provides tooling for running an interactive distributed key generation protocol.
All participants (administrator and key holders) use a command line app to participate in the protocol, and all the communication is hapenning through a smart contract.

Distributed Key Generation for generating the threshold public key is based on [Secure Distributed Key Generation for Discrete-Log Based Cryptosystems
](https://link.springer.com/article/10.1007/s00145-006-0347-3)

## Build Guide

Build with `cargo build (--release)`.
In the beginning of the DKG procedure:
- The list of account addresses belonging to key holders is publicly known
- There is a designated administrator that deploys and initialized the contract

Test with `cargo test`.

All crates require Rust 2021 edition and are tested on the following channels:
- `1.64.0`
In the result of the DKG procedure:
- All key holders have their secret key share locally
- Individual public key shares and master public key are available publicly

If you do not have Rust installed, run: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
This project implements the JF-DKG scheme described in [Secure Distributed Key Generation for Discrete-Log Based Cryptosystems
](https://link.springer.com/article/10.1007/s00145-006-0347-3)

## Android and iOS
## Install

The library compiles to Android and iOS. This has been tested with Rust v1.64.0.
Get latest binaries from the [releases](https://github.com/trilitech/dkg-tooling/releases) page.

To compile to Android:
Alternatively use Docker images:
```
docker run ghcr.io/trilitech/dkg-cli:$RELEASE_TAG -h
```

1. Download Android NDK r21 and unzip it
2. Set the `NDK_HOME` env var to the extracted directory
3. `cd cross`
4. `./create-ndk-standalone`
5. `make android`
## Use

To compile to ios:
3. `cd cross`
4. `make ios`
Check out the [instructions](crates/dkg-cli).

## Directory Structure
## Build from sources

This repository contains several Rust crates that implement the different building blocks of the MPC. The high-level structure of the repository is as follows:
Build with `NO_SOLC_BUILD=1 cargo build --release`.

- [`dkg-cli`](crates/dkg-cli): Rust crate that provides a CLI for the distributed key generation
- [`dkg-core`](crates/dkg-core): Rust crate that provides the implementation utilities for the DKG
- [`threshold-bls`](crates/threshold-bls): (blind) threshold BLS signatures for BLS12-381 and BLS12-377
- [`threshold-bls-ffi`](crates/threshold-bls-ffi): FFI and WASM bindings to `threshold-bls` for cross platform interoperability
All crates require Rust 2021 edition and are tested on the following channels:
- `1.76.0`

If you do not have Rust installed, run: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`

## Disclaimers

Expand Down
19 changes: 8 additions & 11 deletions crates/dkg-cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
FROM cimg/rust:1.65.0

ENV HOME=/home/circleci
ENV PATH=$HOME/bin:$PATH

RUN cd $HOME && git clone https://github.com/m-kus/threshold-bls-rs
RUN mkdir $HOME/bin && wget -q https://github.com/ethereum/solidity/releases/download/v0.6.6/solc-static-linux -O $HOME/bin/solc && chmod u+x $HOME/bin/solc && solc --version
RUN cd $HOME && cd threshold-bls-rs/crates/dkg-cli && RUSTFLAGS="-C target-feature=-crt-static" cargo build --release
FROM rust:1.76
WORKDIR /root
COPY . /root/dkg-tooling/
#RUN git clone https://github.com/trilitech/dkg-tooling
RUN cd $HOME/dkg-tooling/crates/dkg-cli \
&& RUSTFLAGS="-C target-feature=-crt-static" NO_SOLC_BUILD=1 cargo build --release

FROM ubuntu:22.04
COPY --from=0 /home/circleci/threshold-bls-rs/target/release/dkg-cli /dkgbin
WORKDIR /dkg
ENTRYPOINT [ "/dkgbin" ]
COPY --from=0 /root/dkg-tooling/target/release/dkg-cli /usr/bin/
ENTRYPOINT [ "dkg-cli" ]
1 change: 0 additions & 1 deletion crates/dkg-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ in the [Solidity docs](https://solidity.readthedocs.io/en/latest/installing-soli


Install the DKG CLI with `cargo build --release`.
We will use the Alfajores testnet for this example, which you can access by using `https://alfajores-forno.celo-testnet.org` as a `NODE_URL`. You can fund your account by inserting your `address` to the [Alfajores faucet](https://celo.org/developers/faucet).

1. `dkg-cli keygen --path ./keypair`

Expand Down
1 change: 1 addition & 0 deletions crates/dkg-cli/artifacts/dkg.bin

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/dkg-cli/artifacts/dkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"language":"Solidity","sources":{"contracts/DKG.sol":{"content":"// Using the ABIEncoderV2 poses little risk here because we only use it for fetching the byte arrays\n// of shares/responses/justifications\npragma experimental ABIEncoderV2;\npragma solidity ^0.8;\n\ncontract DKG {\n enum UserState {\n CannotRegister,\n CanRegister,\n Registered\n }\n\n /// Mapping of Ethereum Address => UserState for the actions a user can do\n mapping(address => UserState) public userState;\n\n /// Mapping of Ethereum Address => BLS public keys\n mapping(address => bytes) public keys;\n\n /// Mapping of Ethereum Address => DKG Phase 1 Shares\n mapping(address => bytes) public shares;\n\n /// Mapping of Ethereum Address => DKG Phase 2 Responses\n mapping(address => bytes) public responses;\n\n /// Mapping of Ethereum Address => DKG Phase 3 Justifications\n mapping(address => bytes) public justifications;\n\n /// List of registered Ethereum keys (used for conveniently fetching data)\n address[] public participants;\n\n /// The duration of each phase\n uint256 public immutable PHASE_DURATION;\n\n /// The threshold of the DKG\n uint256 public immutable THRESHOLD;\n\n /// If it's 0 then the DKG is still pending start. If >0, it is the DKG's start block\n uint256 public startBlock = 0;\n\n /// The owner of the DKG is the address which can call the `start` function\n address public owner;\n\n /// A registered participant is one whose pubkey's length > 0\n modifier onlyRegistered() {\n require(userState[msg.sender] == UserState.Registered, \"you are not registered!\");\n _;\n }\n\n /// The DKG starts when startBlock > 0\n modifier onlyWhenNotStarted() {\n require(startBlock == 0, \"DKG has already started\");\n _;\n }\n\n constructor(uint256 threshold, uint256 duration) public {\n PHASE_DURATION = duration;\n THRESHOLD = threshold;\n owner = msg.sender;\n }\n\n /// Kickoff function which starts the counter\n function start() external onlyWhenNotStarted {\n require(msg.sender == owner, \"only owner may start the DKG\");\n startBlock = block.number;\n }\n\n /// The administrator must allowlist an addrss for participation in the DKG\n function allowlist(address user) external onlyWhenNotStarted {\n require(msg.sender == owner, \"only owner may allowlist users\");\n\n require(userState[user] == UserState.CannotRegister, \"user is already allowlisted\");\n userState[user] = UserState.CanRegister;\n }\n\n /// This function ties a DKG participant's on-chain address with their BLS Public Key\n function register(bytes calldata blsPublicKey) external onlyWhenNotStarted {\n require(userState[msg.sender] == UserState.CanRegister, \"user is not allowlisted or has already registered\");\n\n participants.push(msg.sender);\n keys[msg.sender] = blsPublicKey;\n\n // the user is now registered\n userState[msg.sender] = UserState.Registered;\n }\n\n /// Participant publishes their data and depending on the phase the data gets inserted\n /// in the shares, responses or justifications mapping. Reverts if the participant\n /// has already published their data for a phase or if the DKG has ended.\n function publish(bytes calldata value) external onlyRegistered {\n uint256 blocksSinceStart = block.number - startBlock;\n\n if (blocksSinceStart <= PHASE_DURATION) {\n require(\n shares[msg.sender].length == 0,\n \"you have already published your shares\"\n );\n shares[msg.sender] = value;\n } else if (blocksSinceStart <= 2 * PHASE_DURATION) {\n require(\n responses[msg.sender].length == 0,\n \"you have already published your responses\"\n );\n responses[msg.sender] = value;\n } else if (blocksSinceStart <= 3 * PHASE_DURATION) {\n require(\n justifications[msg.sender].length == 0,\n \"you have already published your justifications\"\n );\n justifications[msg.sender] = value;\n } else {\n revert(\"DKG has ended\");\n }\n }\n\n // Helpers to fetch data in the mappings. If a participant has registered but not\n // published their data for a phase, the array element at their index is expected to be 0\n\n /// Gets the participants' shares\n function getShares() external view returns (bytes[] memory) {\n bytes[] memory _shares = new bytes[](participants.length);\n for (uint256 i = 0; i < participants.length; i++) {\n _shares[i] = shares[participants[i]];\n }\n\n return _shares;\n }\n\n /// Gets the participants' responses\n function getResponses() external view returns (bytes[] memory) {\n bytes[] memory _responses = new bytes[](participants.length);\n for (uint256 i = 0; i < participants.length; i++) {\n _responses[i] = responses[participants[i]];\n }\n\n return _responses;\n }\n\n /// Gets the participants' justifications\n function getJustifications() external view returns (bytes[] memory) {\n bytes[] memory _justifications = new bytes[](participants.length);\n for (uint256 i = 0; i < participants.length; i++) {\n _justifications[i] = justifications[participants[i]];\n }\n\n return _justifications;\n }\n\n /// Gets the participants' ethereum addresses\n function getParticipants() external view returns (address[] memory) {\n return participants;\n }\n\n /// Gets the participants' BLS keys along with the thershold of the DKG\n function getBlsKeys() external view returns (uint256, bytes[] memory) {\n bytes[] memory _keys = new bytes[](participants.length);\n for (uint256 i = 0; i < participants.length; i++) {\n _keys[i] = keys[participants[i]];\n }\n\n return (THRESHOLD, _keys);\n }\n\n /// Returns the current phase of the DKG.\n function inPhase() public view returns (uint256) {\n if (startBlock == 0) {\n return 0;\n }\n\n uint256 blocksSinceStart = block.number - startBlock;\n\n if (blocksSinceStart <= PHASE_DURATION) {\n return 1;\n }\n\n if (blocksSinceStart <= 2 * PHASE_DURATION) {\n return 2;\n }\n\n if (blocksSinceStart <= 3 * PHASE_DURATION) {\n return 3;\n }\n\n revert(\"DKG Ended\");\n }\n}\n"}},"settings":{"optimizer":{"enabled":false,"runs":200},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"evmVersion":"shanghai","libraries":{}}}
9 changes: 7 additions & 2 deletions crates/dkg-cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ const CONTRACT_PATH: &str = "../../solidity/contracts/DKG.sol";
const CONTRACT_NAME: &str = "DKG";
// Generates the bindings under `src/`
fn main() {
if option_env!("NO_SOLC_BUILD").is_some() {
return;
}

// Only re-run the builder script if the contract changes
println!("cargo:rerun-if-changed={}", PATH);

Expand All @@ -25,7 +29,7 @@ fn main() {
let compiler_output = project.compile().unwrap();
let contract = compiler_output.find(full_path, CONTRACT_NAME).unwrap();

let mut f = File::create("dkg.bin").expect("could not create DKG bytecode file");
let mut f = File::create("artifacts/dkg.bin").expect("could not create DKG bytecode file");
let bytecode: String = contract.bytecode.clone().unwrap().object.encode_hex();

f.write_all(bytecode.as_bytes())
Expand All @@ -46,7 +50,8 @@ fn main() {
let verification_input = project
.standard_json_input(project.sources_path().join("DKG.sol"))
.unwrap();
let mut j = File::create("dkg.json").expect("could not create DKG standard sol input file");
let mut j =
File::create("artifacts/dkg.json").expect("could not create DKG standard sol input file");

j.write_all(
serde_json::to_string(&verification_input)
Expand Down
Loading
Loading