This commit is contained in:
AbcSxyZ 2021-09-30 04:17:03 +02:00 committed by GitHub
commit eb0740bd3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 433 additions and 757 deletions

643
contrib/gitian-build.sh Executable file → Normal file
View file

@ -1,406 +1,389 @@
#!/bin/bash
# Copyright (c) 2016 The Bitcoin Core developers # Copyright (c) 2016 The Bitcoin Core developers
# Copyright (c) 2021 The Dogecoin Core developers # Copyright (c) 2021 The Dogecoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Systems to build
DESCRIPTORS=('osx' 'win' 'linux')
SIGN_DESCRIPTORS=('win-signed' 'osx-signed')
# Gitian properties
export USE_DOCKER=0
export USE_LXC=0
# Dependencies
ossPatchUrl="https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch"
ossPatchHash="a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911"
ossTarUrl="https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/osslsigncode/1.7.1-1/osslsigncode_1.7.1.orig.tar.gz"
ossTarHash="f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9"
macosSdkUrl="https://bitcoincore.org/depends-sources/sdks/MacOSX10.11.sdk.tar.gz"
macosSdkHash="bec9d089ebf2e2dd59b1a811a38ec78ebd5da18cbbcd6ab39d1e59f64ac5033f"
# What to do # What to do
sign=false
verify=false verify=false
build=false build=false
setupenv=false buildSigned=false
commit=false
# Systems to build test=false
linux=true enableCache=false
windows=true
osx=true
# Other Basic variables # Other Basic variables
SIGNER= SIGNER=
VERSION= VERSION=
commit=false
url=https://github.com/dogecoin/dogecoin url=https://github.com/dogecoin/dogecoin
proc=2 proc=2
mem=2000 mem=2000
lxc=true
osslTarUrl=https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/osslsigncode/1.7.1-1/osslsigncode_1.7.1.orig.tar.gz
osslPatchUrl=https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch
scriptName=$(basename -- "$0") scriptName=$(basename -- "$0")
signProg="gpg --detach-sign" outputDir=$(pwd)/gitian-output
commitFiles=true
# Help Message # Help message
read -d '' usage <<- EOF read -r -d '' usage <<-EOF
Usage: $scriptName [-c|u|v|b|s|B|o|h|j|m|] signer version Usage: $scriptName [options] version
Run this script from the directory containing the dogecoin, gitian-builder, gitian.sigs, and dogecoin-detached-sigs. Standalone script to perform the gitian build of Dogecoin Core. Perform
deterministic build for multiples Operating System, using Docker, LXC or
KVM for virtualization. Sign binaries using PGP.
Use https://github.com/devrandom/gitian-builder to manage the process.
Arguments: Arguments:
signer GPG signer to sign each build assert file version Version number, commit, or branch to build. If building a
version Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified commit or branch, the -c option must be specified
Options: Options:
-c|--commit Indicate that the version argument is for a commit or branch --setup Setup the gitian building environment.
-u|--url Specify the URL of the repository. Default is https://github.com/dogecoin/dogecoin -b|--build Do the gitian build
-v|--verify Verify the gitian build -B|--build-signed Build signed binaries for MacOS and Windows
-b|--build Do a gitian build -s|--sign name Sign builded executables with GPG using user ID
-s|--sign Make signed binaries for Windows and Mac OSX -v|--verify Verify the gitian build
-B|--buildsign Build both signed and unsigned binaries --lxc Use LXC instead of KVM
-o|--os Specify which Operating Systems the build is for. Default is lwx. l for linux, w for windows, x for osx --docker Use Docker instead of KVM
-j Number of processes to use. Default 2 -o|--os lwx Specify which Operating Systems the build is for. Default is lwx,
-m Memory to allocate in MiB. Default 2000 l for Linux, w for Windows, x for MacOS
--kvm Use KVM instead of LXC -j proc Number of processes to use. Default $proc
--setup Setup the gitian building environment. Uses KVM. If you want to use lxc, use the --lxc option. Only works on Debian-based systems (Ubuntu, Debian) -m n Memory to allocate in MiB. Default $mem
--detach-sign Create the assert file for detached signing. Will not commit anything. --enable-cache Use local apt-cacher server. If you need to specify host, use
--no-commit Do not commit anything to git MIRROR_HOST environment variable
-h|--help Print this help message -c|--commit Indicate that the version argument is for a commit or branch
-u|--url repo Specify the URL of the repository. Default is https://github.com/dogecoin/dogecoin
--test CI TEST. Uses Docker
-h|--help Print this help message
EOF EOF
# Get options and arguments # Get options and arguments
while :; do while :; do
case $1 in case $1 in
# Verify # Verify
-v|--verify) -v | --verify)
verify=true verify=true
;; ;;
# Build # Build
-b|--build) -b | --build)
build=true build=true
;; ;;
# Sign binaries # Build signed binaries
-s|--sign) -B | --build-signed)
sign=true buildSigned=true
;;
# Build then Sign
-B|--buildsign)
sign=true
build=true
;; ;;
# PGP Signer # PGP Signer
-S|--signer) -s | --sign)
if [ -n "$2" ] if [ -n "$2" ]; then
then SIGNER=$2
SIGNER=$2 shift
shift else
else echo 'Error: "--sign" requires a PGP signer.'
echo 'Error: "--signer" requires a non-empty argument.' exit 1
exit 1 fi
fi ;;
;;
# Operating Systems # Operating Systems
-o|--os) -o | --os)
if [ -n "$2" ] if [ -n "$2" ]; then
then DESCRIPTORS=()
linux=false SIGN_DESCRIPTORS=()
windows=false if [[ "$2" == *"l"* ]]; then
osx=false DESCRIPTORS+=('linux')
if [[ "$2" = *"l"* ]] fi
then if [[ "$2" == *"w"* ]]; then
linux=true DESCRIPTORS+=('win')
fi SIGN_DESCRIPTORS+=('win-signed')
if [[ "$2" = *"w"* ]] fi
then if [[ "$2" == *"x"* ]]; then
windows=true DESCRIPTORS+=('osx')
fi SIGN_DESCRIPTORS+=('osx-signed')
if [[ "$2" = *"x"* ]] fi
then shift
osx=true else
fi echo 'Error: "--os" specify os: l (linux), w (windows), or x (Mac OSX)'
shift exit 1
else fi
echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)\n'
exit 1
fi
;;
# Help message
-h|--help)
echo "$usage"
exit 0
;;
# Commit or branch
-c|--commit)
commit=true
;;
# Number of Processes
-j)
if [ -n "$2" ]
then
proc=$2
shift
else
echo 'Error: "-j" requires an argument'
exit 1
fi
;;
# Memory to allocate
-m)
if [ -n "$2" ]
then
mem=$2
shift
else
echo 'Error: "-m" requires an argument'
exit 1
fi
;;
# URL
-u)
if [ -n "$2" ]
then
url=$2
shift
else
echo 'Error: "-u" requires an argument'
exit 1
fi
;;
# kvm
--kvm)
lxc=false
;; ;;
# Detach sign # Help message
--detach-sign) -h | --help)
signProg="true" echo "$usage"
commitFiles=false exit 0
;; ;;
# Commit files # Commit or branch
--no-commit) -c | --commit)
commitFiles=false commit=true
;;
# Number of Processes
-j)
if [ -n "$2" ]; then
proc=$2
shift
else
echo 'Error: "-j" requires an argument'
exit 1
fi
;;
# Memory to allocate
-m)
if [ -n "$2" ]; then
mem=$2
shift
else
echo 'Error: "-m" requires an argument'
exit 1
fi
;;
# lxc
--lxc)
USE_LXC=1
;;
# docker
--docker)
USE_DOCKER=1
;;
# apt cacher
--enable-cache)
enableCache=true
;;
# URL
-u)
if [ -n "$2" ]; then
url=$2
shift
else
echo 'Error: "-u" requires an argument'
exit 1
fi
;; ;;
# Setup # Setup
--setup) --setup)
setup=true setup=true
;; ;;
*) # Default case: If no more options then break out of the loop. --test)
break test=true
;;
*) # Default case: If no more options then break out of the loop.
break ;;
esac esac
shift shift
done done
# Set up LXC # Download specific file and verify hash
if [[ $lxc = true ]] function download_file () {
then local filename=$(basename $1)
export USE_LXC=1
export LXC_BRIDGE=br0 if [ ! -f $filename ]; then
wget $1
fi
# Verify file signature
echo "$2 $filename" | sha256sum -c --status
if [ $? != 0 ]; then
echo "$scriptName: Signature for $filename don't match."
exit 1
fi
}
function move_build_files() {
find build/out -type f -exec mv '{}' $outputDir/dogecoin-binaries/${VERSION}/ \;
}
function download_descriptor() {
if [[ ! $1 == 'test' ]]; then
uri="${url/github.com/raw.githubusercontent.com}"/"$2"/contrib/gitian-descriptors/gitian-"$1".yml
echo "Downloading descriptor ${1} ${uri}"
wget $uri -O gitian-"$1".yml || exit 1
else
# CI tests
cp ../ci/descriptor/"$1".yml gitian-"$1".yml || exit 1
fi
}
### Test configuration ###
if [[ $test == true ]]; then
if [[ $commit == true ]]; then
VERSION="f80bfe9068ac1a0619d48dad0d268894d926941e"
else
VERSION="1.14.3"
fi
DESCRIPTORS=('test')
SIGN_DESCRIPTORS=()
COMMIT=$VERSION
SIGNER="signer"
SHA256SUM="0d519f6ade0e601617a7c44b764eeae35a8784070c3e44f542011956f1743459"
fi fi
# Check for OSX SDK ### Arguments checks ####
if [[ ! -e "gitian-builder/inputs/MacOSX10.11.sdk.tar.gz" && $osx == true ]]
then echo "Using ${proc} CPU and ${mem} RAM"
echo "Cannot build for OSX, SDK does not exist. Will build for other OSes"
osx=false # Control the selection of a single virtualisation software
if [ $(($USE_LXC + $USE_DOCKER)) -ge 2 ]; then
echo "$scriptName: Specify a single virtualisation solution between Docker, LXC or KVM."
exit
fi fi
# Get signer if [ -n "$SIGNER" ]; then
if [[ -n"$1" ]] echo "Testing GPG Keys available..."
then result=$(gpg --list-secret-keys --keyid-format=long | grep sec | grep -v revoked | grep "" -c)
SIGNER=$1 if [[ $result == "" ]]; then
shift echo "No GPG keys available..."
echo "Please follow this documentation: https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification/generating-a-new-gpg-key"
exit 1
fi
mkdir -p $outputDir/sigs
fi fi
# Get version # Get version
if [[ -n "$1" ]] if [ -n "$1" ]; then
then
VERSION=$1 VERSION=$1
COMMIT=$VERSION
shift
fi
# Check that a signer is specified # Use tag or commit within repository
if [[ $SIGNER == "" ]] if [[ $commit == false ]]; then
then COMMIT=v$VERSION
echo "$scriptName: Missing signer." else
echo "Try $scriptName --help for more information" COMMIT=$VERSION
exit 1
fi
# Check that a version is specified
if [[ $VERSION == "" ]]
then
echo "$scriptName: Missing version."
echo "Try $scriptName --help for more information"
exit 1
fi
# Add a "v" if no -c
if [[ $commit = false ]]
then
COMMIT="v${VERSION}"
fi
echo ${COMMIT}
# Setup build environment
if [[ $setup = true ]]
then
sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm qemu-utils
# GIT --date=format-local support
MIN_GIT_VERSION=2.7.0
LASTEST_GIT_VERSION=2.32.0
if ! (echo a version ${MIN_GIT_VERSION}; git --version) | sort -Vk3 | tail -1 | grep -q git; then
sudo apt-get install build-essential make libssl-dev libghc-zlib-dev libcurl4-gnutls-dev libexpat1-dev gettext unzip
wget https://github.com/git/git/archive/v${LASTEST_GIT_VERSION}.zip -O v${LASTEST_GIT_VERSION}.zip
unzip v${LASTEST_GIT_VERSION}.zip
pushd ./git-${LASTEST_GIT_VERSION}
make -j "${proc}" prefix=/usr/local all
make -j "${proc}" prefix=/usr/local install
popd
fi fi
else
echo "$scriptName: Missing version, see --help for more information."
exit 1
fi
# GIT CLONE ### Setup ###
if [[ $setup == true ]]; then
git clone https://github.com/dogecoin/gitian.sigs.git git clone https://github.com/dogecoin/gitian.sigs.git
git clone https://github.com/dogecoin/dogecoin-detached-sigs.git git clone https://github.com/dogecoin/dogecoin-detached-sigs.git
git clone https://github.com/devrandom/gitian-builder.git git clone https://github.com/devrandom/gitian-builder.git
pushd ./gitian-builder pushd ./gitian-builder
if [[ -n "$USE_LXC" ]]
#Download dependencies
mkdir -p inputs
pushd inputs
download_file $ossPatchUrl $ossPatchHash
download_file $ossTarUrl $ossTarHash
download_file $macosSdkUrl $macosSdkHash
popd
#Check if apt-cacher should be enabled
if [ -z "$MIRROR_HOST" ] && [[ $enableCache = false ]]; then
cacher_option="--disable-apt-cacher"
fi
#Prepare containers depending of virtualization solution: lxc, docker, kvm
if [ "$USE_LXC" -eq 1 ]
then then
sudo apt-get install lxc sudo apt-get install -y lxc
bin/make-base-vm --suite trusty --arch amd64 --lxc bin/make-base-vm --suite trusty --arch amd64 --lxc $(echo $cacher_option)
elif [ "$USE_DOCKER" -eq 1 ]; then
bin/make-base-vm --suite trusty --arch amd64 --docker $(echo $cacher_option)
else else
bin/make-base-vm --suite trusty --arch amd64 bin/make-base-vm --suite trusty --arch amd64 $(echo $cacher_option)
fi fi
popd popd
fi fi
# Set up build # Download descriptors
pushd ./dogecoin mkdir -p ./gitian-descriptors/
git fetch
git checkout ${COMMIT} pushd gitian-descriptors || exit 1
if [[ $build == true || $verify == true ]]; then
for descriptor in "${DESCRIPTORS[@]}"; do
download_descriptor "$descriptor" "$COMMIT"
done
fi
if [[ $buildSigned == true ]]; then
for sign_descriptor in "${SIGN_DESCRIPTORS[@]}"; do
download_descriptor "$sign_descriptor" "$COMMIT"
done
fi
popd popd
# Build ### Build ###
if [[ $build = true ]]
then
# Make output folder
mkdir -p ./dogecoin-binaries/${VERSION}
# Build Dependencies
echo ""
echo "Building Dependencies"
echo ""
pushd ./gitian-builder
mkdir -p inputs
wget -N -P inputs $osslPatchUrl
wget -N -P inputs $osslTarUrl
make -j "${proc}" -C ../dogecoin/depends download SOURCES_PATH=`pwd`/cache/common
# Linux if [[ $build == true ]]; then
if [[ $linux = true ]] # Make output folder
then mkdir -p $outputDir/dogecoin-binaries/"$VERSION"
echo ""
echo "Compiling ${VERSION} Linux"
echo ""
./bin/gbuild -j ${proc} -m ${mem} --commit dogecoin=${COMMIT} --url dogecoin=${url} ../dogecoin/contrib/gitian-descriptors/gitian-linux.yml
./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../dogecoin/contrib/gitian-descriptors/gitian-linux.yml
mv build/out/dogecoin-*.tar.gz build/out/src/dogecoin-*.tar.gz ../dogecoin-binaries/${VERSION}
fi
# Windows
if [[ $windows = true ]]
then
echo ""
echo "Compiling ${VERSION} Windows"
echo ""
./bin/gbuild -j ${proc} -m ${mem} --commit dogecoin=${COMMIT} --url dogecoin=${url} ../dogecoin/contrib/gitian-descriptors/gitian-win.yml
./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../dogecoin/contrib/gitian-descriptors/gitian-win.yml
mv build/out/dogecoin-*-win-unsigned.tar.gz inputs/dogecoin-win-unsigned.tar.gz
mv build/out/dogecoin-*.zip build/out/dogecoin-*.exe ../dogecoin-binaries/${VERSION}
fi
# Mac OSX
if [[ $osx = true ]]
then
echo ""
echo "Compiling ${VERSION} Mac OSX"
echo ""
./bin/gbuild -j ${proc} -m ${mem} --commit dogecoin=${COMMIT} --url dogecoin=${url} ../dogecoin/contrib/gitian-descriptors/gitian-osx.yml
./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../dogecoin/contrib/gitian-descriptors/gitian-osx.yml
mv build/out/dogecoin-*-osx-unsigned.tar.gz inputs/dogecoin-osx-unsigned.tar.gz
mv build/out/dogecoin-*.tar.gz build/out/dogecoin-*.dmg ../dogecoin-binaries/${VERSION}
fi
popd
if [[ $commitFiles = true ]] pushd ./gitian-builder || exit 1
then
# Commit to gitian.sigs repo # Clean dogecoin git directory because of old caching
echo "" if [ -d inputs/dogecoin/ ]; then
echo "Committing ${VERSION} Unsigned Sigs" echo "Cleaning Dogecoin directory..."
echo "" rm -rf inputs/dogecoin/
pushd gitian.sigs fi
git add ${VERSION}-linux/${SIGNER}
git add ${VERSION}-win-unsigned/${SIGNER} for descriptor in "${DESCRIPTORS[@]}"; do
git add ${VERSION}-osx-unsigned/${SIGNER} echo ""
git commit -a -m "Add ${VERSION} unsigned sigs for ${SIGNER}" echo "Compiling ${VERSION} ${descriptor}"
popd echo ""
./bin/gbuild -j "$proc" -m "$mem" --commit dogecoin="$COMMIT" --url dogecoin="$url" ../gitian-descriptors/gitian-"$descriptor".yml || exit 1
if [ -n "$SIGNER" ]; then
./bin/gsign --signer "$SIGNER" --release "$VERSION"-"$descriptor" \
--destination $outputDir/sigs/ ../gitian-descriptors/gitian-"$descriptor".yml
fi fi
move_build_files
done
popd || exit 1
fi fi
# Verify the build # Build signed binaries
if [[ $verify = true ]] if [[ $buildSigned == true ]]; then
then pushd gitian-builder || exit 1
# Linux
pushd ./gitian-builder
echo ""
echo "Verifying v${VERSION} Linux"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../dogecoin/contrib/gitian-descriptors/gitian-linux.yml
# Windows
echo ""
echo "Verifying v${VERSION} Windows"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../dogecoin/contrib/gitian-descriptors/gitian-win.yml
# Mac OSX
echo ""
echo "Verifying v${VERSION} Mac OSX"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../dogecoin/contrib/gitian-descriptors/gitian-osx.yml
# Signed Windows
echo ""
echo "Verifying v${VERSION} Signed Windows"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../dogecoin/contrib/gitian-descriptors/gitian-osx-signer.yml
# Signed Mac OSX
echo ""
echo "Verifying v${VERSION} Signed Mac OSX"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../dogecoin/contrib/gitian-descriptors/gitian-osx-signer.yml
popd
fi
# Sign binaries for sign_descriptor in "${SIGN_DESCRIPTORS[@]}"; do
if [[ $sign = true ]] echo ""
then echo "Compiling Binary ${VERSION} ${sign_descriptor}"
echo ""
pushd ./gitian-builder ./bin/gbuild --skip-image --upgrade --commit signature="$COMMIT" ../gitian-descriptors/gitian-"$sign_descriptor".yml || exit 1
# Sign Windows if [ -n "$SIGNER" ]; then
if [[ $windows = true ]] ./bin/gsign --signer "$SIGNER" --release "$VERSION"-"$sign_descriptor" \
then --destination $outputDir/sigs/ ../gitian-descriptors/gitian-"$sign_descriptor".yml
echo ""
echo "Signing ${VERSION} Windows"
echo ""
./bin/gbuild -i --commit signature=${COMMIT} ../dogecoin/contrib/gitian-descriptors/gitian-win-signer.yml
./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../dogecoin/contrib/gitian-descriptors/gitian-win-signer.yml
mv build/out/dogecoin-*win64-setup.exe ../dogecoin-binaries/${VERSION}
mv build/out/dogecoin-*win32-setup.exe ../dogecoin-binaries/${VERSION}
fi
# Sign Mac OSX
if [[ $osx = true ]]
then
echo ""
echo "Signing ${VERSION} Mac OSX"
echo ""
./bin/gbuild -i --commit signature=${COMMIT} ../dogecoin/contrib/gitian-descriptors/gitian-osx-signer.yml
./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../dogecoin/contrib/gitian-descriptors/gitian-osx-signer.yml
mv build/out/dogecoin-osx-signed.dmg ../dogecoin-binaries/${VERSION}/dogecoin-${VERSION}-osx.dmg
fi
popd
if [[ $commitFiles = true ]]
then
# Commit Sigs
pushd gitian.sigs
echo ""
echo "Committing ${VERSION} Signed Sigs"
echo ""
git add ${VERSION}-win-signed/${SIGNER}
git add ${VERSION}-osx-signed/${SIGNER}
git commit -a -m "Add ${VERSION} signed binary sigs for ${SIGNER}"
popd
fi fi
move_build_files
done
popd || exit 1
fi
### Signatures Verification ###
if [[ $verify == true ]]; then
pushd ./gitian-builder || exit 1
for descriptor in "${DESCRIPTORS[@]}"; do
echo ""
echo "Verifying v${VERSION} ${descriptor}"
echo ""
./bin/gverify -v -d ../gitian.sigs/ -r "${VERSION}"-"$descriptor" ../gitian-descriptors/gitian-"$descriptor".yml
done
popd || exit 1
fi fi

View file

@ -1,466 +1,159 @@
Gitian building
================
*Setup instructions for a Gitian build of Dogecoin Core using a Debian VM or physical system.* # Gitian building
Gitian is the deterministic build process that is used to build the Dogecoin > *Gitian is a secure source-control oriented software distribution method. This means you can download trusted binaries that are verified by multiple builders.*
Core executables. It provides a way to be reasonably sure that the Source : [Gitian.org](https://gitian.org/)
executables are really built from the source on GitHub. It also makes sure that
the same, tested dependencies are used and statically built into the executable.
Multiple developers build the source code by following a specific descriptor Gitian is a deterministic build process that is used to release Dogecoin Core executables. It use same dependencies in a virtualized environment to build binaries. This let independent builders compare binaries hashes to verify and sign released executables.
("recipe"), cryptographically sign the result, and upload the resulting signature.
These results are compared and only if they match, the build is accepted and uploaded
to dogecoin.com.
More independent Gitian builders are needed, which is why this guide exists. To reduce probability of compromised binaries during releases, more independent gitian builders are needed !
It is preferred you follow these steps yourself instead of using someone else's
VM image to avoid 'contaminating' the build.
Table of Contents Participate and help to secure the process by following this guide.
------------------
- [Create a new VirtualBox VM](#create-a-new-virtualbox-vm) ### Table of contents
- [Connecting to the VM](#connecting-to-the-vm)
- [Setting up Debian for Gitian building](#setting-up-debian-for-gitian-building)
- [Installing Gitian](#installing-gitian)
- [Setting up the Gitian image](#setting-up-the-gitian-image)
- [Getting and building the inputs](#getting-and-building-the-inputs)
- [Building Dogecoin Core](#building-dogecoin-core)
- [Building an alternative repository](#building-an-alternative-repository)
- [Signing externally](#signing-externally)
- [Uploading signatures](#uploading-signatures)
Preparing the Gitian builder host 1. [Install dependencies](#install-dependencies)
--------------------------------- * [Common dependencies](#common-dependencies)
* [Docker](#docker)
* [LXC](#lxc)
* [KVM](#kvm)
* [Apt-cacher](#apt-cacher)
2. [Usage](#usage)
* [Syntax](#syntax)
* [Example](#example)
* [Signing externally](#signing-externally)
3. [Publish signatures](#publish-signatures)
The first step is to prepare the host environment that will be used to perform the Gitian builds. ## Install dependencies
This guide explains how to set up the environment, and how to start the builds.
Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available. To perform a gitian build, you can use different virtualization software : Docker, KVM or LXC. Dependencies will change according to your choice.
Any kind of virtualization can be used, for example:
- [VirtualBox](https://www.virtualbox.org/) (covered by this guide)
- [KVM](http://www.linux-kvm.org/page/Main_Page)
- [LXC](https://linuxcontainers.org/), see also [Gitian host docker container](https://github.com/gdm85/tenku/tree/master/docker/gitian-bitcoin-host/README.md).
You can also install Gitian on actual hardware instead of using virtualization. You need to install some required dependencies whatever you will choose.
Create a new VirtualBox VM Use your packet manager to install them : `apt`, `brew`, `dnf`, `pacman`...
---------------------------
In the VirtualBox GUI click "New" and choose the following parameters in the wizard:
![](gitian-building/create_new_vm.png) ### Common dependencies
- Type: Linux, Debian (64-bit)
![](gitian-building/create_vm_memsize.png)
- Memory Size: at least 3000MB, anything less and the build might not complete.
![](gitian-building/create_vm_hard_disk.png)
- Hard Disk: Create a virtual hard disk now
![](gitian-building/create_vm_hard_disk_file_type.png)
- Hard Disk file type: Use the default, VDI (VirtualBox Disk Image)
![](gitian-building/create_vm_storage_physical_hard_disk.png)
- Storage on physical hard disk: Dynamically Allocated
![](gitian-building/create_vm_file_location_size.png)
- File location and size: at least 40GB; as low as 20GB *may* be possible, but better to err on the safe side
- Click `Create`
After creating the VM, we need to configure it.
- Click the `Settings` button, then go to the `Network` tab. Adapter 1 should be attached to `NAT`.
![](gitian-building/network_settings.png)
- Click `Advanced`, then `Port Forwarding`. We want to set up a port through which we can reach the VM to get files in and out.
- Create a new rule by clicking the plus icon.
![](gitian-building/port_forwarding_rules.png)
- Set up the new rule the following way:
- Name: `SSH`
- Protocol: `TCP`
- Leave Host IP empty
- Host Port: `22222`
- Leave Guest IP empty
- Guest Port: `22`
- Click `Ok` twice to save.
Get the [Debian 8.x net installer](http://cdimage.debian.org/mirror/cdimage/archive/8.5.0/amd64/iso-cd/debian-8.5.0-amd64-netinst.iso) (a more recent minor version should also work, see also [Debian Network installation](https://www.debian.org/CD/netinst/)).
This DVD image can be [validated](https://www.debian.org/CD/verify) using a SHA256 hashing tool, for example on
Unixy OSes by entering the following in a terminal:
echo "ad4e8c27c561ad8248d5ebc1d36eb172f884057bfeb2c22ead823f59fa8c3dff debian-8.5.0-amd64-netinst.iso" | sha256sum -c
# (must return OK)
Then start the VM. On the first launch you will be asked for a CD or DVD image. Choose the downloaded ISO.
![](gitian-building/select_startup_disk.png)
Installing Debian
------------------
This section will explain how to install Debian on the newly created VM.
- Choose the non-graphical installer. We do not need the graphical environment; it will only increase installation time and disk usage.
![](gitian-building/debian_install_1_boot_menu.png)
**Note**: Navigating in the Debian installer:
To keep a setting at the default and proceed, just press `Enter`.
To select a different button, press `Tab`.
- Choose locale and keyboard settings (doesn't matter, you can just go with the defaults or select your own information)
![](gitian-building/debian_install_2_select_a_language.png)
![](gitian-building/debian_install_3_select_location.png)
![](gitian-building/debian_install_4_configure_keyboard.png)
- The VM will detect network settings using DHCP, this should all proceed automatically
- Configure the network:
- Hostname `debian`.
- Leave domain name empty.
![](gitian-building/debian_install_5_configure_the_network.png)
- Choose a root password and enter it twice (remember it for later)
![](gitian-building/debian_install_6a_set_up_root_password.png)
- Name the new user `debian` (the full name doesn't matter, you can leave it empty)
- Set the account username as `debian`
![](gitian-building/debian_install_7_set_up_user_fullname.png)
![](gitian-building/debian_install_8_set_up_username.png)
- Choose a user password and enter it twice (remember it for later)
![](gitian-building/debian_install_9_user_password.png)
- The installer will set up the clock using a time server; this process should be automatic
- Set up the clock: choose a time zone (depends on the locale settings that you picked earlier; specifics don't matter)
![](gitian-building/debian_install_10_configure_clock.png)
- Disk setup
- Partitioning method: Guided - Use the entire disk
![](gitian-building/debian_install_11_partition_disks.png)
- Select disk to partition: SCSI1 (0,0,0)
![](gitian-building/debian_install_12_choose_disk.png)
- Partition Disks -> *All files in one partition*
![](gitian-building/all_files_in_one_partition.png)
- Finish partitioning and write changes to disk -> *Yes* (`Tab`, `Enter` to select the `Yes` button)
![](gitian-building/debian_install_14_finish.png)
![](gitian-building/debian_install_15_write_changes.png)
- The base system will be installed, this will take a minute or so
- Choose a mirror (any will do)
![](gitian-building/debian_install_16_choose_a_mirror.png)
- Enter proxy information (unless you are on an intranet, leave this empty)
![](gitian-building/debian_install_18_proxy_settings.png)
- Wait a bit while 'Select and install software' runs
- Participate in popularity contest -> *No*
- Choose software to install. We need just the base system.
- Make sure only 'SSH server' and 'Standard System Utilities' are checked
- Uncheck 'Debian Desktop Environment' and 'Print Server'
![](gitian-building/debian_install_19_software_selection.png)
- Install the GRUB boot loader to the master boot record? -> Yes
![](gitian-building/debian_install_20_install_grub.png)
- Device for boot loader installation -> ata-VBOX_HARDDISK
![](gitian-building/debian_install_21_install_grub_bootloader.png)
- Installation Complete -> *Continue*
- After installation, the VM will reboot and you will have a working Debian VM. Congratulations!
![](gitian-building/debian_install_22_finish_installation.png)
After Installation
-------------------
The next step in the guide involves logging in as root via SSH.
SSH login for root users is disabled by default, so we'll enable that now.
Login to the VM using username `root` and the root password you chose earlier.
You'll be presented with a screen similar to this.
![](gitian-building/debian_root_login.png)
Type:
Following dependencies are required to run `gitian-build.sh`:
``` ```
sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config git ruby wget
``` ```
and press enter. Then,
Optionally, to sign or verify binaries :
``` ```
/etc/init.d/ssh restart pgp
``` ```
and enter to restart SSH. Logout by typing 'logout' and pressing 'enter'. *To create a PGP key to sign files, see : https://gnupg.org/gph/en/manual.html#INTRO.
You will need to specify your [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html), find it using `gpg -k`.*
Connecting to the VM ### Docker
----------------------
After the VM has booted you can connect to it using SSH, and files can be copied from and to the VM using a SFTP utility. Follow [Docker official documentation](https://docs.docker.com/engine/install/) to install it for your operating system.
Connect to `localhost`, port `22222` (or the port configured when installing the VM).
On Windows you can use [putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](http://winscp.net/eng/index.php).
For example, to connect as `root` from a Linux command prompt use Make sure your user can run `docker` command without root privilege by being in the docker group :
```bash
$ sudo usermod -aG docker $(whoami)
$ ssh root@localhost -p 22222 # Enable group without logging out
The authenticity of host '[localhost]:22222 ([127.0.0.1]:22222)' can't be established. $ newgrp docker
RSA key fingerprint is ae:f5:c8:9f:17:c6:c7:1b:c2:1b:12:31:1d:bb:d0:c7. ```
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:22222' (RSA) to the list of known hosts.
root@localhost's password: (enter root password configured during install)
The programs included with the Debian GNU/Linux system are free software; Then use `--docker` option with `gitian-build.sh`.
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent ### LXC
permitted by applicable law. Install the following package :
root@debian:~# ```
lxc
```
Replace `root` with `debian` to log in as user. Then use `--lxc` option with `gitian-build.sh`.
Setting up Debian for Gitian building ### KVM
--------------------------------------
In this section we will be setting up the Debian installation for Gitian building. [Documentation not available, help is welcome]
First we need to log in as `root` to set up dependencies and make sure that our ### Apt-cacher
user can use the sudo command. Type/paste the following in the terminal:
Disabled by default, `apt-cacher` enable to cache locally downloaded dependencies to save resources.
You will need the following package :
```
apache2 apt-cacher-ng
```
You can use your local server by using `--enable-apt-cacher`, or define `MIRROR_HOST` if you need to specify the server address.
> **Be nice:** Please use cache for intensive ressource usage to save mirrors bandwith.
## Usage
`gitian-build.sh` is a standalone script, it can be downloaded and run outside of Dogecoin Core repository.
It can download dependency files for the [Gitian](https://github.com/devrandom/gitian-builder), build and optionally sign binaries, or verify signatures.
Binaries and signatures will be created in folder `gitian-output/`.
### Syntax
```bash ```bash
apt-get install git ruby sudo apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl $ ./gitian-build.sh [options] version
adduser debian sudo
# See help menu for available options
$ ./gitian-build.sh --help
Arguments:
version Version number, commit, or branch to build. If building a
commit or branch, the -c option must be specified
Options:
--setup Setup dependencies for the gitian building environment. Uses Docker
-b|--build Do the gitian build
-B|--build-signed Build signed binaries for MacOS and Windows
-s|--sign name Sign builded executables with GPG
-v|--verify Verify the gitian build
--lxc Use LXC instead of KVM
--docker Use Docker instead of KVM
-o|--os lwx Specify which Operating Systems the build is for. Default is lwx,
l for Linux, w for Windows, x for MacOS
-j proc Number of processes to use. Default 2
-m n Memory to allocate in MiB. Default 2000
--enable-cache Use local apt-cacher server. If you need to specify host, use
MIRROR_HOST environment variable
-c|--commit Indicate that the version argument is for a commit or branch
-u|--url repo Specify the URL of the repository. Default is https://github.com/dogecoin/dogecoin
--test CI TEST. Uses Docker
-h|--help Print this help message
``` ```
Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds: ### Example
The entire gitian flow can be performed step by step, example using docker :
```bash ```bash
# the version of lxc-start in Debian needs to run as root, so make sure # Download Gitian dependencies
# that the build script can execute it without providing a password $ ./gitian-build.sh --docker --setup 1.14.4
echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc
echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc # Build & sign executables
# make /etc/rc.local script that sets up bridge between guest and host $ ./gitian-build.sh --docker --build --sign SIGNER 1.14.4
echo '#!/bin/sh -e' > /etc/rc.local
echo 'brctl addbr br0' >> /etc/rc.local # Verify signatures
echo 'ifconfig br0 10.0.3.2/24 up' >> /etc/rc.local $ ./gitian-build.sh --verify 1.14.4
echo 'iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE' >> /etc/rc.local
echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/rc.local
echo 'exit 0' >> /etc/rc.local
# make sure that USE_LXC is always set when logging in as debian,
# and configure LXC IP addresses
echo 'export USE_LXC=1' >> /home/debian/.profile
echo 'export GITIAN_HOST_IP=10.0.3.2' >> /home/debian/.profile
echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/debian/.profile
reboot
``` ```
At the end the VM is rebooted to make sure that the changes take effect. The steps in this Or to do everything at once :
section only need to be performed once.
Installing Gitian
------------------
Re-login as the user `debian` that was created during installation.
The rest of the steps in this guide will be performed as that user.
There is no `python-vm-builder` package in Debian, so we need to install it from source ourselves,
```bash ```bash
wget http://archive.ubuntu.com/ubuntu/pool/universe/v/vm-builder/vm-builder_0.12.4+bzr494.orig.tar.gz $ ./gitian-build.sh --docker --setup --build --sign SIGNER --verify 1.14.4
echo "76cbf8c52c391160b2641e7120dbade5afded713afaa6032f733a261f13e6a8e vm-builder_0.12.4+bzr494.orig.tar.gz" | sha256sum -c
# (verification -- must return OK)
tar -zxvf vm-builder_0.12.4+bzr494.orig.tar.gz
cd vm-builder-0.12.4+bzr494
sudo python setup.py install
cd ..
``` ```
**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. ### Signing externally
Clone the git repositories for dogecoin and Gitian.
```bash
git clone https://github.com/devrandom/gitian-builder.git
git clone https://github.com/dogecoin/dogecoin.git
git clone https://github.com/dogecoin/gitian.sigs.git
```
Setting up the Gitian image
-------------------------
Gitian needs a virtual image of the operating system to build in.
Currently this is Ubuntu Trusty x86_64.
This image will be copied and used every time that a build is started to
make sure that the build is deterministic.
Creating the image will take a while, but only has to be done once.
Execute the following as user `debian`:
```bash
cd gitian-builder
bin/make-base-vm --lxc --arch amd64 --suite trusty
```
There will be a lot of warnings printed during the build of the image. These can be ignored.
**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*.
Getting and building the inputs
--------------------------------
At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-building.sh -b signer 0.13.0"). Otherwise ignore this.
Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-create-inputs-first-time-or-when-dependency-versions-change)
in the dogecoin repository under 'Fetch and create inputs' to install sources which require
manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache
and offline git repositories' which will fetch the remaining files required for building
offline.
Building Dogecoin Core
----------------
To build Dogecoin Core (for Linux, OS X and Windows) just follow the steps under 'perform
Gitian builds' in [doc/release-process.md](release-process.md#perform-gitian-builds) in the dogecoin repository.
This may take some time as it will build all the dependencies needed for each descriptor.
These dependencies will be cached after a successful build to avoid rebuilding them when possible.
At any time you can check the package installation and build progress with
```bash
tail -f var/install.log
tail -f var/build.log
```
Output from `gbuild` will look something like
Initialized empty Git repository in /home/debian/gitian-builder/inputs/dogecoin/.git/
remote: Counting objects: 57959, done.
remote: Total 57959 (delta 0), reused 0 (delta 0), pack-reused 57958
Receiving objects: 100% (57959/57959), 53.76 MiB | 484.00 KiB/s, done.
Resolving deltas: 100% (41590/41590), done.
From https://github.com/dogecoin/dogecoin
... (new tags, new branch etc)
--- Building for trusty amd64 ---
Stopping target if it is up
Making a new image copy
stdin: is not a tty
Starting target
Checking if target is up
Preparing build environment
Updating apt-get repository (log in var/install.log)
Installing additional packages (log in var/install.log)
Grabbing package manifest
stdin: is not a tty
Creating build script (var/build-script)
lxc-start: Connection refused - inotify event with no name (mask 32768)
Running build script (log in var/build.log)
Building an alternative repository
-----------------------------------
If you want to do a test build of a pull on GitHub it can be useful to point
the Gitian builder at an alternative repository, using the same descriptors
and inputs.
For example:
```bash
URL=https://github.com/laanwj/dogecoin.git
COMMIT=2014_03_windows_unicode_path
./bin/gbuild --commit dogecoin=${COMMIT} --url dogecoin=${URL} ../dogecoin/contrib/gitian-descriptors/gitian-linux.yml
./bin/gbuild --commit dogecoin=${COMMIT} --url dogecoin=${URL} ../dogecoin/contrib/gitian-descriptors/gitian-win.yml
./bin/gbuild --commit dogecoin=${COMMIT} --url dogecoin=${URL} ../dogecoin/contrib/gitian-descriptors/gitian-osx.yml
```
Building fully offline
-----------------------
For building fully offline including attaching signatures to unsigned builds, the detached-sigs repository
and the dogecoin git repository with the desired tag must both be available locally, and then gbuild must be
told where to find them. It also requires an apt-cacher-ng which is fully-populated but set to offline mode, or
manually disabling gitian-builder's use of apt-get to update the VM build environment.
To configure apt-cacher-ng as an offline cacher, you will need to first populate its cache with the relevant
files. You must additionally patch target-bin/bootstrap-fixup to set its apt sources to something other than
plain archive.ubuntu.com: us.archive.ubuntu.com works.
So, if you use LXC:
```bash
export PATH="$PATH":/path/to/gitian-builder/libexec
export USE_LXC=1
cd /path/to/gitian-builder
./libexec/make-clean-vm --suite trusty --arch amd64
LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get update
LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root \
-e DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install \
$( sed -ne '/^packages:/,/[^-] .*/ {/^- .*/{s/"//g;s/- //;p}}' ../dogecoin/contrib/gitian-descriptors/*|sort|uniq )
LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get -q -y purge grub
LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
```
And then set offline mode for apt-cacher-ng:
```
/etc/apt-cacher-ng/acng.conf
[...]
Offlinemode: 1
[...]
service apt-cacher-ng restart
```
Then when building, override the remote URLs that gbuild would otherwise pull from the Gitian descriptors::
```bash
cd /some/root/path/
git clone https://github.com/dogecoin/dogecoin-detached-sigs.git
BTCPATH=/some/root/path/dogecoin
SIGPATH=/some/root/path/dogecoin-detached-sigs
./bin/gbuild --url dogecoin=${BTCPATH},signature=${SIGPATH} ../dogecoin/contrib/gitian-descriptors/gitian-win-signer.yml
```
Signing externally
-------------------
If you want to do the PGP signing on another device, that's also possible; just define `SIGNER` as mentioned If you want to do the PGP signing on another device, that's also possible; just define `SIGNER` as mentioned
and follow the steps in the build process as normal. and follow the steps in the build process as normal.
gpg: skipped "laanwj": secret key not available ```bash
$ gpg: skipped "laanwj": secret key not available
```
When you execute `gsign` you will get an error from GPG, which can be ignored. Copy the resulting `.assert` files When you execute `gsign` you will get an error from GPG, which can be ignored. Copy the resulting `.assert` files in `gitian.sigs` to your signing machine and do
in `gitian.sigs` to your signing machine and do
```bash ```bash
gpg --detach-sign ${VERSION}-linux/${SIGNER}/dogecoin-linux-build.assert gpg --detach-sign ${VERSION}-linux/${SIGNER}/dogecoin-linux-build.assert
@ -468,12 +161,12 @@ in `gitian.sigs` to your signing machine and do
gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/dogecoin-osx-build.assert gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/dogecoin-osx-build.assert
``` ```
This will create the `.sig` files that can be committed together with the `.assert` files to assert your This will create the `.sig` files that can be committed together with the `.assert` files to assert your Gitian build.
Gitian build.
Uploading signatures ## Publish signatures
---------------------
After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the Gitian signatures for each release are added to https://github.com/dogecoin/gitian.sigs.
[dogecoin/gitian.sigs](https://github.com/dogecoin/gitian.sigs/) repository, or if that's not possible create a pull
request. You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. `gitian-build.sh` will create signatures inside `gitian-output/sigs/` folder. Create a pull request to [dogecoin/gitian.sigs](https://github.com/dogecoin/gitian.sigs) to publish your signatures, the `.assert` and `.assert.sig` files.
**When your PR is merged, you will officially be a *Gitian Builder of Dogecoin* !**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB