├── .gitignore ├── .gitlab-ci.yml ├── Dockerfile ├── LICENSE.txt ├── README.md ├── aleph ├── __init__.py ├── actions │ ├── __init__.py │ ├── create_unit.py │ └── poset_syncing.py ├── config.py ├── const.py ├── crypto │ ├── __init__.py │ ├── byte_utils.py │ ├── crp.py │ ├── keys.py │ ├── threshold_coin.py │ └── threshold_signatures.py ├── data_structures │ ├── __init__.py │ ├── poset.py │ ├── tx.py │ ├── unit.py │ └── userDB.py ├── log_analyzer │ ├── __init__.py │ ├── dumped_poset_analyzer.py │ ├── generate_bar_plots.py │ ├── log_analyzer.py │ └── run_analyzer.py ├── main.py ├── network │ ├── __init__.py │ ├── channel.py │ ├── network.py │ └── tx_traffic.py ├── process.py ├── test │ ├── __init__.py │ ├── data │ │ ├── __init__.py │ │ ├── light_nodes_public_keys │ │ └── simple.dag │ ├── test_message_signing.py │ ├── test_poset_below.py │ ├── test_poset_break_ties.py │ ├── test_poset_compliance.py │ ├── test_poset_create_unit.py │ ├── test_poset_floor.py │ ├── test_poset_level.py │ ├── test_poset_threshold_coin.py │ ├── test_threshold_coin.py │ ├── test_threshold_signatures.py │ └── test_unit_signing.py └── utils │ ├── __init__.py │ ├── dag.py │ ├── dag_utils.py │ ├── generic_test.py │ ├── plot.py │ └── timer.py ├── benchmarks ├── signing.py ├── threshold_coin.py └── toss_coin.py ├── docs ├── Makefile ├── build │ └── html │ │ ├── _static │ │ ├── basic.css │ │ ├── classic.css │ │ ├── default.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── jquery-3.2.1.js │ │ ├── jquery.js │ │ ├── language_data.js │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── sidebar.js │ │ ├── underscore-1.3.1.js │ │ └── underscore.js │ │ ├── actions │ │ ├── actions.html │ │ ├── create_unit.html │ │ └── poset_syncing.html │ │ ├── crypto │ │ ├── crp.html │ │ ├── crypto.html │ │ ├── keys.html │ │ ├── threshold_coin.html │ │ └── threshold_signatures.html │ │ ├── data_structures │ │ ├── data_structures.html │ │ ├── poset.html │ │ ├── tx.html │ │ └── unit.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── intro.html │ │ ├── network │ │ ├── channel.html │ │ ├── net.html │ │ └── network.html │ │ ├── objects.inv │ │ ├── process.html │ │ ├── py-modindex.html │ │ ├── search.html │ │ └── searchindex.js ├── make.bat └── source │ ├── actions │ ├── actions.rst │ ├── create_unit.rst │ └── poset_syncing.rst │ ├── aleph_1920x1080.jpg │ ├── conf.py │ ├── crypto │ ├── crp.rst │ ├── crypto.rst │ ├── keys.rst │ ├── threshold_coin.rst │ └── threshold_signatures.rst │ ├── data_structures │ ├── data_structures.rst │ ├── poset.rst │ ├── tx.rst │ └── unit.rst │ ├── index.rst │ ├── intro.rst │ ├── network │ ├── channel.rst │ ├── net.rst │ └── network.rst │ └── process.rst ├── examples ├── plot.py ├── random_forking_10_30_5.txt └── random_nonforking_10_30.txt ├── experiments ├── aws │ ├── aws_deps.sh │ ├── const.py │ ├── fabfile.py │ ├── key_pairs │ │ ├── aleph.fingerprint │ │ ├── aleph.pem │ │ └── aleph.pem.pub │ ├── light_nodes_public_keys │ ├── set_env.sh │ ├── setup.sh │ ├── shell.py │ └── utils.py └── simple_ec2_test.py ├── reports ├── external │ └── generate_plots.py └── internal │ ├── bar_plots │ ├── big │ │ ├── bytes_sent_per_sec.png │ │ ├── create_delay.png │ │ ├── create_ord_del.png │ │ ├── decision_height.png │ │ ├── memory_MiB.png │ │ ├── n_maximal.png │ │ ├── n_parents.png │ │ ├── poset_decision_height.png │ │ ├── sync_delay.png │ │ ├── time_create.png │ │ ├── time_per_sync.png │ │ ├── txps.png │ │ └── units_sent_sync.png │ ├── final_exp1 │ │ ├── bytes_sent_per_sec.png │ │ ├── create_delay.png │ │ ├── create_ord_del.png │ │ ├── decision_height.png │ │ ├── n_maximal.png │ │ ├── n_parents.png │ │ ├── n_units_decision.png │ │ ├── poset_decision_height.png │ │ ├── sync_delay.png │ │ ├── time_per_sync.png │ │ └── units_sent_sync.png │ ├── final_exp2 │ │ ├── bytes_sent_per_sec.png │ │ ├── create_ord_del.png │ │ ├── memory_MiB.png │ │ ├── time_per_sync.png │ │ └── txps.png │ └── levels.png │ └── report.tex ├── setup.py └── tests ├── byzantine_linear_ordering_network.py ├── byzantine_process.py ├── linear_ordering.py ├── linear_ordering_network.py ├── make_keys.py ├── process_network.py ├── read_dumped_poset.py └── threshold_coin_distribution.py /.gitignore: -------------------------------------------------------------------------------- 1 | __* 2 | !__init__.py 3 | *.log 4 | .cache 5 | *.png 6 | *.out 7 | *.txt 8 | *.aux 9 | *.bbl 10 | *.blg 11 | *.pdf 12 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: alpine:latest 2 | 3 | pages: 4 | script: 5 | - mkdir public 6 | - cp -r docs/build/html/* public 7 | artifacts: 8 | paths: 9 | - public 10 | only: 11 | - master 12 | - devel 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | # prepare a build environment 4 | RUN apk update 5 | RUN apk add --no-cache \ 6 | bash \ 7 | make \ 8 | gcc \ 9 | g++ \ 10 | musl-dev \ 11 | bison \ 12 | flex \ 13 | mpc \ 14 | gmp-dev \ 15 | openssl-dev \ 16 | git \ 17 | libpng-dev \ 18 | libffi-dev \ 19 | freetype-dev 20 | 21 | WORKDIR /root 22 | RUN wget https://crypto.stanford.edu/pbc/files/pbc-0.5.14.tar.gz && \ 23 | tar -xvf pbc-0.5.14.tar.gz && \ 24 | cd pbc-0.5.14 && ./configure && make >/dev/null 2>&1 && make install 25 | 26 | RUN git clone https://github.com/JHUISI/charm.git && cd charm && ./configure.sh && make install >/dev/null 2>&1 27 | 28 | COPY setup.py /root 29 | RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -e . 30 | 31 | # build succesfull 32 | 33 | COPY aleph /root/aleph 34 | WORKDIR /root/aleph 35 | 36 | ENV N_CORES=1 N=16 37 | 38 | RUN rm -rf __pycache__ unitTest/__pycache__ 39 | 40 | CMD pytest -n $N_CORES unitTest 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aleph Zero: Aleph Protocol Proof-of-Concept 2 | 3 | ![aleph logo](docs/source/aleph_1920x1080.jpg "Aleph logo") 4 | 5 | Aleph is an asynchronous and Byzantine fault tolerant DAG-based consensus protocol aimed at ordering messages (transactions). It has been designed to operate continuously under conditions where there are no bound on message-delivery delay and under the assumption that there is a significant probability of malicious behavior, making it an excellent fit for blockchain-related applications. For more information, check [webpage](https://alephzero.org). 6 | 7 | This repository is a proof-of-concept implementation, it is not meant for production deployment. It is released as a reference for the main implementation in Go that will be published in the future. The initial version of the repository was based on an [old paper](https://arxiv.org/abs/1810.05256), while the more recent one relies on a [new paper](https://arxiv.org/abs/1908.05156). 8 | 9 | # Results from experiments run on AWS 10 | 11 | The following results come from experiments performed on 128 nodes of Amazon Web Services distributed uniformly between 8 different regions on 5 continents. 12 | We tested the performance under various loads of the system: 13 | 14 | |load|txps|latency| 15 | |---|---:|---:| 16 | | small | 72.1 | 5.1s| 17 | | big | 9476.7 | 15.8s | 18 | | heavy | 93419.0 | 20.5s | 19 | 20 | For more results, details of the setup, and discussions check our [reports](https://gitlab.com/alephledger/proof-of-concept/tree/master/reports). 21 | 22 | # Documentation 23 | 24 | [The documentation](https://alephledger.gitlab.io/proof-of-concept) describes only the algorithmic and technical parts of the implementation. For the conceptual understanding of the protocol and the implementation please read the above papers. 25 | 26 | # Merge requests 27 | 28 | This repository is only an exploratory playground and we are done with it now. No further developments will be made on our side. If you want to play with it, go ahead and fork it! 29 | 30 | # Installation 31 | 32 | The implementation requires Python 3.7. The following script creates a virtual environment for Python 3.7 in home directory and installs Aleph and its dependencies there. The script was prepared for Ubuntu 18.04. 33 | - run `bash experiments/aws/setup.sh` 34 | - enter py37 env `source $HOME/p37/bin/activate` 35 | - install aleph `pip install -e .` 36 | Remember to activate py37 env and `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib` in every terminal you play with Aleph. 37 | 38 | # Unit tests 39 | To run unit tests please use the following command: `pytest-xdist -n $n_cores aleph` 40 | 41 | # Experiments 42 | 43 | There are two types of experiments that can be performed: 44 | 1. Local: run `python tests/linear_ordering_network.py` 45 | 2. Remote using AWS EC2: 46 | - Create an account on AWS and set up credentials and a default region as described [here](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html#configuration). 47 | - Then, go to `cd experiments/aws` and install additional packages `bash aws_deps.sh`. 48 | - Run `python shell.py`. This opens a shell with procedures orchestrating experiments. The main procedure is 49 | `run_protocol(n_processes, regions, restricted, instance_type)` that runs `n_processes` in specified `regions`, where some of the regions can be `restricted`, and uses EC2 machines of `instance_type`. 50 | - The most basic experiment can be run with `run_protocol(8, badger_regions(), {}, 't2.micro')`. It spawns 8 machines in 8 different regions on 5 continents. As of time of writing, AWS EC2 was providing users with a limited time of free usage of machines of type `t2.micro` and some quota for free storage and data transfer, so such an experiment can be conducted free of charge. 51 | - The parameters of the protocol are defined in the file `const.py`. 52 | - To check whether an experiment has finished, use the procedure `reached_max_level` that returns the number of instances that finished their run. 53 | - After the experiment is finished, the logs containing useful data of the experiment can be downloaded with `get_logs`. 54 | 55 | # Analyzing logs 56 | After collecting the logs, the performance can be analyzed as follows: 57 | 1. A single log with data on instance labeled with pid 58 | `python aleph/log_analyzer/run_analyzer.py aleph.log pid` 59 | 2. All logs 60 | `python aleph/log_analyzer/run_analyzer.py ALL log-dir [report-dir]` 61 | 62 | # License 63 | Aleph Python implementation is released under an LGPL version 3 license. See the `LICENSE.txt` for details. 64 | -------------------------------------------------------------------------------- /aleph/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | -------------------------------------------------------------------------------- /aleph/actions/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from .create_unit import create_unit 15 | from .poset_syncing import poset_info, units_to_send, dehash_parents 16 | -------------------------------------------------------------------------------- /aleph/actions/create_unit.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | '''This module implements unit creation.''' 15 | 16 | import random 17 | import logging 18 | 19 | from aleph.data_structures.unit import Unit 20 | import aleph.const as consts 21 | 22 | def _create_dealing_unit(poset, creator_id, txs): 23 | U = Unit(creator_id, [], txs) 24 | if poset.use_tcoin: 25 | poset.add_tcoin_to_dealing_unit(U) 26 | return U 27 | 28 | def _nonforking_creator(poset, V): 29 | return len(poset.max_units_per_process[V.creator_id]) == 1 30 | 31 | def _parent_candidates(poset, parents, level): 32 | return list(reversed([V for V in poset.max_units if V.level == level and V not in parents and _nonforking_creator(poset, V)])) 33 | 34 | def _combine_parents(parents, new_parents): 35 | if not new_parents: 36 | return parents 37 | level = new_parents[0].level 38 | lower_parents = [V for V in parents if V.level <= level] 39 | higher_parents = [V for V in parents if V.level > level] 40 | return lower_parents + new_parents + higher_parents 41 | 42 | def _pick_more_parents(poset, parents, level, num_parents): 43 | parent_candidates = _parent_candidates(poset, parents, level) 44 | non_visible_primes = poset.get_all_prime_units_by_level(level) 45 | for V in parents: 46 | non_visible_primes = [W for W in non_visible_primes if not poset.below(W, V)] 47 | new_parents = [] 48 | for V in parent_candidates: 49 | if len(new_parents) + len(parents) == num_parents: 50 | return _combine_parents(parents, new_parents) 51 | # the 3 lines below can be optimized (by a constant factor) but it is left as it is for simplicity 52 | if any(poset.below(W, V) for W in non_visible_primes): 53 | new_parents.append(V) 54 | non_visible_primes = [W for W in non_visible_primes if not poset.below(W, V)] 55 | return _combine_parents(parents, new_parents) 56 | 57 | def create_unit(poset, creator_id, txs, num_parents = None): 58 | ''' 59 | Creates a new unit and stores txs in it. It uses only maximal units in the poset as parents (with the possible exception for the self_predecessor). 60 | This parent selection strategy has the following properties: 61 | - the created unit satisfies the expand-primes rule, 62 | - the main objective is for the new unit to see as many units as possible, but at the same time to not have too many parents 63 | NOTE: this strategy is most effective when num_parents is quite high, ideally unrestricted. 64 | 65 | :param Poset poset: poset in which the new unit is created 66 | :param int creator_id: id of process creating the new unit 67 | :param list txs: list of correct transactions 68 | :param int num_parents: maximum number of distinct parents (lower bound is always 2) 69 | :returns: the new-created unit, or None if it is not possible to create a compliant unit using this strategy 70 | ''' 71 | num_parents = consts.N_PARENTS if num_parents is None else num_parents 72 | logger = logging.getLogger(consts.LOGGER_NAME) 73 | logger.info(f"create: {creator_id} attempting to create a unit.") 74 | 75 | if not poset.max_units_per_process[creator_id]: 76 | U = _create_dealing_unit(poset, creator_id, txs) 77 | logger.info(f"create: {creator_id} created its dealing unit.") 78 | return U 79 | 80 | assert len(poset.max_units_per_process[creator_id]) == 1, "It appears we have created a fork." 81 | 82 | # choose parents for the new unit 83 | U_self_predecessor = poset.max_units_per_process[creator_id][0] 84 | level_predecessor = U_self_predecessor.level 85 | level_poset = poset.level_reached 86 | parents = [U_self_predecessor] 87 | # we start by picking parents of level = level_poset 88 | parents = _pick_more_parents(poset, parents, level_poset, num_parents) 89 | 90 | if level_poset > level_predecessor and len(parents) < num_parents: 91 | # We expect here that level_poset = level_predecessor + 1. 92 | # We will add a bunch of parents of level = level_predecessor, the reason to do that is to make the new unit "see" as many 93 | # units as possible. This in turn is to make the poset "better connected" which helps in fast proofs of popularity for timing units. 94 | parents = _pick_more_parents(poset, parents, level_predecessor, num_parents) 95 | 96 | if len(parents) < 2: 97 | return None 98 | 99 | U = Unit(creator_id, parents, txs) 100 | 101 | if poset.use_tcoin: 102 | # we need to call prepare_unit to fill some fields necessary for determining which coin shares to add 103 | poset.prepare_unit(U) 104 | if poset.is_prime(U) and U.level >= consts.ADD_SHARES: 105 | poset.add_coin_shares(U) 106 | 107 | return U 108 | -------------------------------------------------------------------------------- /aleph/config.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import aleph.const as consts 15 | 16 | import charm.toolbox.pairinggroup 17 | PAIRING_GROUP = charm.toolbox.pairinggroup.PairingGroup('MNT224') 18 | # initialize group generator 19 | GENERATOR = PAIRING_GROUP.hash('gengen', charm.toolbox.pairinggroup.G2) 20 | # precompute exponentiation table to speed up computations 21 | GENERATOR.initPP() 22 | 23 | import pickle 24 | pickle.DEFAULT_PROTOCOL = 4 25 | 26 | import logging 27 | log_format = '[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s [%(filename)s:%(lineno)d]' 28 | logging.basicConfig(filename='other.log', 29 | level=logging.DEBUG, 30 | format=log_format, 31 | filemode='w') 32 | logger = logging.getLogger(consts.LOGGER_NAME) 33 | logger.setLevel(logging.DEBUG) 34 | fh = logging.FileHandler('aleph.log', mode='w') 35 | fh.setLevel(logging.DEBUG) 36 | fh.setFormatter(logging.Formatter(log_format)) 37 | logger.addHandler(fh) 38 | 39 | import time 40 | # use gmt time for logging 41 | logging.Formatter.converter = time.gmtime 42 | -------------------------------------------------------------------------------- /aleph/const.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | N_PARENTS = 10 # maximal number of parents a unit can have 15 | 16 | CREATE_DELAY = 2.0 # delay after creating a new unit 17 | STEP_SIZE = 0.14 # a number in (0,1) describing how aggresive is the create_delay adjusting mechanism, large = aggresive 18 | 19 | SYNC_INIT_DELAY = 0.015625 # delay after initianing a sync with other processes 20 | 21 | 22 | N_RECV_SYNC = 10 # number of allowed parallel received syncs 23 | N_INIT_SYNC = 10 # number of allowed parallel initiated syncs 24 | 25 | TXPU = 1 # number of transactions per unit 26 | TX_LIMIT = 1000000 # limit of all txs generated for one process 27 | 28 | LEVEL_LIMIT = 20 # maximal level after which process shuts down 29 | UNITS_LIMIT = None # maximal number of units that are constructed 30 | SYNCS_LIMIT = None # maximal number of syncs that are performed 31 | 32 | USE_TCOIN = 1 # whether to use threshold coin 33 | PRECOMPUTE_POPULARITY = 0 # precompute popularity proof to ease computational load of Poset.compute_vote procedure 34 | ADAPTIVE_DELAY = 1 # whether to use the adaptive strategy of determining create_delay 35 | 36 | VOTING_LEVEL = 3 # level at which the first voting round occurs, this is "t" from the write-up 37 | PI_DELTA_LEVEL = 12 # level at which to switch from the "fast" to the pi_delta algorithm 38 | ADD_SHARES = PI_DELTA_LEVEL - 1 # level at which to start adding coin shares to units, it's safe to make it PI_DELTA_LEVEL - 1 39 | 40 | HOST_IP = '127.0.0.1' # default ip address of a process 41 | HOST_PORT = 8888 # default port of incoming syncs 42 | 43 | LOGGER_NAME = 'aleph' # name of our logger and logfile 44 | TX_SOURCE = 'tx_source_gen' # source of txs 45 | -------------------------------------------------------------------------------- /aleph/crypto/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from .crp import CommonRandomPermutation 15 | from .byte_utils import xor, sha3_hash, extract_bit 16 | from .keys import SigningKey, VerifyKey 17 | from .threshold_coin import ThresholdCoin 18 | from .threshold_signatures import generate_keys, SecretKey, VerificationKey 19 | -------------------------------------------------------------------------------- /aleph/crypto/byte_utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import hashlib 15 | 16 | 17 | def extract_bit(bytestring, bit_index): 18 | ''' 19 | Returns the bit_index'th bit of bytestring. 20 | It is assumed that bit_index < 8*len(bytestring) since bytestring is an array of bytes, each 8 bits long. 21 | 22 | :param bytes bytestring: bytestring from which the bit is to be extracted 23 | :param int bit_index: index of bit to be extracted. 24 | ''' 25 | assert bit_index < 8*len(bytestring), "Attempting to extract a bit with too large of an index." 26 | 27 | byte_index = bit_index // 8 28 | index_within_byte = bit_index % 8 29 | mask = (1 << index_within_byte) 30 | if bytestring[byte_index] & mask: 31 | return 1 32 | return 0 33 | 34 | 35 | def sha3_hash(bytestring): 36 | ''' 37 | Returns the sha3_256 hash of the bytestring. 38 | 39 | :param bytes bytestring: bytestring of which the hash is calculated. 40 | ''' 41 | return hashlib.sha3_256(bytestring).digest() 42 | 43 | 44 | def xor(bytes1, bytes2): 45 | ''' 46 | Returns a xor of two bytestrings bytes1, bytes2. The length of the result is the max of their lengths. 47 | If one of them is shorter it is rotated cyclically to obtain a string of matching length. 48 | 49 | :param bytes bytes1: xor's first argument 50 | :param bytes bytes2: xor's second argument 51 | ''' 52 | assert bytes1 and bytes2, "An attempt to xor an empty bytestring" 53 | if len(bytes1) < len(bytes2): 54 | bytes1, bytes2 = bytes2, bytes1 55 | 56 | result = list(bytes1[:]) 57 | len_bytes2 = len(bytes2) 58 | for i in range(len(bytes1)): 59 | result[i] ^= bytes2[i % len_bytes2] 60 | 61 | return bytes(result) 62 | -------------------------------------------------------------------------------- /aleph/crypto/crp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from .byte_utils import xor 15 | from .byte_utils import sha3_hash 16 | 17 | 18 | class CommonRandomPermutation: 19 | ''' 20 | This class represents common random permutation defined in the whitepaper. 21 | 22 | :param list public_keys: list of all public keys in hex format 23 | :param function hashing_function: hashing function used for generating common random permutations -- assumed to take input and output a bytestring if None hashing_function is provided then it uses sha3_hash i.e. hashlib.sha512 24 | ''' 25 | 26 | def __init__(self, public_keys_hex, hashing_function=None): 27 | self.public_keys_hex = public_keys_hex 28 | self.hashing_function = hashing_function 29 | self.cache = {} 30 | self.cache_size = 20 31 | 32 | def _hash(self, bytestring): 33 | if self.hashing_function is None: 34 | return sha3_hash(bytestring) 35 | return self.hashing_function(bytestring) 36 | 37 | def add_to_cache(self, level, permutation): 38 | ''' 39 | Adds an element to cache and shrinks the size of cache if the size becomes > 2*cache_size 40 | 41 | :param int level: level for which permutation is cached 42 | :param list permutation: permutation of the set n to be cached 43 | ''' 44 | 45 | self.cache[level] = permutation 46 | if len(self.cache) > 2*self.cache_size: 47 | # the cache grew too large -- fetch its elements sorted from highest indices to lowest 48 | cache_items = list(reversed(sorted(self.cache.items()))) 49 | # remove all but cache_size largest indices 50 | self.cache = dict(cache_items[:self.cache_size]) 51 | 52 | def index_of(self, item, level): 53 | ''' 54 | Outputs the position in the permutation crp[level] of item. 55 | 56 | :param int item: the number whose position we seek 57 | :param int level: the level of the permutation of interest 58 | :returns: the position of item in the permutation at this level 59 | ''' 60 | sigma = self.__getitem__(level) 61 | return sigma.index(item) 62 | 63 | def __getitem__(self, level): 64 | ''' 65 | Returns common random permutation for level level. 66 | 67 | :param int level: level for which the permutation is returned 68 | ''' 69 | if level in self.cache: 70 | return self.cache[level] 71 | 72 | xor_all = bytes([0]) 73 | for pk in self.public_keys_hex: 74 | xor_all = xor(xor_all, pk) 75 | 76 | seeds = [self._hash(pk + (str(level).encode())) for pk in self.public_keys_hex] 77 | seeds = [xor(xor_all, x) for x in seeds] 78 | 79 | indexed_seeds = zip(seeds, range(len(seeds))) 80 | indexed_seeds = sorted(list(indexed_seeds)) 81 | permutation = [ind for (seed, ind) in indexed_seeds] 82 | self.add_to_cache(level, permutation) 83 | return permutation 84 | -------------------------------------------------------------------------------- /aleph/crypto/keys.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import nacl.signing 15 | 16 | 17 | class SigningKey: 18 | ''' 19 | Implements signing (private) key. 20 | 21 | :param string seed_hex: seed used for instantiating this class; if None a key is generated. 22 | ''' 23 | 24 | def __init__(self, seed_hex=None): 25 | if seed_hex is not None: 26 | self.secret_key = nacl.signing.SigningKey(seed_hex, encoder=nacl.encoding.HexEncoder) 27 | else: 28 | self.secret_key = nacl.signing.SigningKey.generate() 29 | 30 | def sign(self, message): 31 | ''' 32 | Returns signed message. 33 | 34 | :param bytes message: message to be signed 35 | ''' 36 | 37 | if isinstance(message, str): 38 | message = message.encode() 39 | 40 | return self.secret_key.sign(message).signature 41 | 42 | def to_hex(self): 43 | ''' Returns hex representation of the secret. It is used for serialization. ''' 44 | 45 | return nacl.encoding.HexEncoder.encode(self.secret_key._seed) 46 | 47 | 48 | class VerifyKey: 49 | ''' Implements verification (public) key. 50 | 51 | :param nacl.signing.VerifyKey verify_key: key used to instantiate this class 52 | ''' 53 | 54 | def __init__(self, verify_key): 55 | 56 | assert isinstance(verify_key, nacl.signing.VerifyKey), 'Wrong class used to instantiation, use nacl.signing.VerifyKey' 57 | self.verify_key = verify_key 58 | 59 | @staticmethod 60 | def from_SigningKey(secret_key): 61 | ''' 62 | A factory generating object of this class from secret_key 63 | 64 | :param SigningKey secret_key: key used to create VerifyKey 65 | :returns: VerifyKey object 66 | ''' 67 | 68 | return VerifyKey(secret_key.secret_key.verify_key) 69 | 70 | @staticmethod 71 | def from_hex(verify_key_hex): 72 | ''' 73 | A vactory generating object of this class from hex representation 74 | 75 | :param string verify_key_hex: hex representation of VerifyKey 76 | :returns: VerifyKey object 77 | ''' 78 | 79 | return VerifyKey(nacl.signing.VerifyKey(verify_key_hex, encoder=nacl.encoding.HexEncoder)) 80 | 81 | def verify_signature(self, signature, message): 82 | ''' 83 | Verifies signature of the message. 84 | 85 | :param bytes signature: signature to verify 86 | :param bytes message: message that was supposedly signed 87 | :returns: True if signature is correct, False otherwise 88 | ''' 89 | 90 | if isinstance(message, str): 91 | message = message.encode() 92 | try: 93 | self.verify_key.verify(message, signature) 94 | except nacl.exceptions.BadSignatureError: 95 | return False 96 | 97 | return True 98 | 99 | def to_hex(self): 100 | ''' 101 | Returns hex representation of this object. It is used for serialization. 102 | ''' 103 | 104 | return self.verify_key.encode(encoder=nacl.encoding.HexEncoder) 105 | -------------------------------------------------------------------------------- /aleph/crypto/threshold_coin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from base64 import decodebytes 15 | from random import randrange 16 | from charm.core.math.pairing import hashPair 17 | 18 | 19 | class ThresholdCoin: 20 | ''' 21 | Implements dual threshold coin described in the whitepaper. 22 | 23 | :param int dealer_id: identification number of a process dealing this coin, from 0 to n-1 24 | :param int process_id: identification number of a process using this coin 25 | :param int n_processes: number of processes 26 | :param int threshold: number of shares required to toss the coin, has to satisfy n_processes//3 < threshold <= n_processes 27 | :param VerifyKey verification_key: key for combining shares 28 | :param SigningKey secret_key: key for generating a share of a coin toss 29 | ''' 30 | 31 | def __init__(self, dealer_id, process_id, n_processes, threshold, secret_key, verification_key): 32 | self.dealer_id = dealer_id 33 | self.process_id = process_id 34 | self.n_processes = n_processes 35 | self.threshold = threshold 36 | self.secret_key = secret_key 37 | self.verification_key = verification_key 38 | 39 | def check_validity(self): 40 | ''' 41 | Checks if this threshold coin is valid. 42 | ''' 43 | 44 | msg_hash = self.verification_key.hash_fct(str(randrange(0, 1000))) 45 | coin_share = self.secret_key.generate_share(msg_hash) 46 | 47 | return self.verification_key.verify_share(coin_share, self.process_id, msg_hash) 48 | 49 | def create_coin_share(self, nonce): 50 | ''' 51 | :param int nonce: nonce for the coin share 52 | :returns: coin share for the nonce 53 | ''' 54 | msg_hash = self.verification_key.hash_fct(str(nonce)) 55 | coin_share = self.secret_key.generate_share(msg_hash) 56 | 57 | return coin_share 58 | 59 | def verify_coin_share(self, coin_share, process_id, nonce): 60 | ''' 61 | :param CoinShare coin_share: coin_share which validity is checked 62 | :param int process_id: identification number of a process that generated the coin_share 63 | :param int nonce: nonce for which the coin_share was generated 64 | :returns: True if coin_share is valid and False otherwise 65 | ''' 66 | 67 | msg_hash = self.verification_key.hash_fct(str(nonce)) 68 | 69 | return self.verification_key.verify_share(coin_share, process_id, msg_hash) 70 | 71 | 72 | def combine_coin_shares(self, shares, nonce): 73 | ''' 74 | Combines the coin shares by forming a threshold signature and taking its 1st bit, subsequently it verifies the result. 75 | NOTE: combining shares should always succeed except when some of the shares were invalid or the dealer was dishonest, in which case the toss might be biased and should ideally be discarded 76 | 77 | :param dict shares: keys are processes ids, values are shares (group G1 elements) 78 | :param string nonce: the nonce the shares were created for -- necessary for verifying the result of combining 79 | :returns: pair (int, bool) : (coin toss in {0,1}) , (whether combining shares was succesful) 80 | ''' 81 | 82 | # there are enough shares of a coin 83 | assert len(shares) == self.threshold, 'Not enough shares for combining' 84 | 85 | signature = self.verification_key.combine_shares(shares) 86 | hex_string = hashPair(signature).decode() 87 | # we just use the first bit as the coin toss 88 | coin_value = bytes.fromhex(hex_string)[0] % 2 89 | 90 | # verify the result 91 | nonce_hash = self.verification_key.hash_fct(nonce) 92 | correctness = self.verification_key.verify_signature(signature, nonce_hash) 93 | 94 | return (coin_value, correctness) 95 | -------------------------------------------------------------------------------- /aleph/crypto/threshold_signatures.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from functools import reduce 15 | 16 | from charm.toolbox.pairinggroup import ZR, G1, pair 17 | 18 | from aleph.config import PAIRING_GROUP, GENERATOR 19 | 20 | # The implementation is based on: Boldyreva, 2002 https://eprint.iacr.org/2002/118.pdf 21 | # Possible alternative implementation: Shoup, 2000 http://eprint.iacr.org/1999/011 22 | 23 | 24 | def generate_keys(n_parties, threshold): 25 | ''' 26 | Generates one verification key and n_parties secret keys. 27 | 28 | :param int n_parties: number of parties that need secret keys 29 | :param int threshold: number of signature shares required for generating a signature 30 | ''' 31 | 32 | group = PAIRING_GROUP 33 | 34 | # pick a generator of the group 35 | gen = GENERATOR 36 | 37 | # pick a set of coefficients 38 | coef = group.random(ZR, threshold) 39 | secret = coef[-1] 40 | 41 | # generate secret keys 42 | sks = [_poly(coef, x) for x in range(1, n_parties+1)] 43 | 44 | # generate underlying verification keys 45 | vk = gen ** secret 46 | vks = [gen ** scr for scr in sks] 47 | 48 | verification_key = VerificationKey(threshold, vk, vks) 49 | secret_keys = [SecretKey(sk) for sk in sks] 50 | 51 | return verification_key, secret_keys 52 | 53 | 54 | class VerificationKey: 55 | ''' 56 | An object used for verifying shares and signatures and for combining shares into signatures. 57 | 58 | :param int threshold: number of signature shares needed to generate a signature 59 | :param int vk: global verification key 60 | :param list vks: verification keys corresponding to secret keys of all parties 61 | ''' 62 | 63 | def __init__(self, threshold, vk, vks): 64 | self.threshold = threshold 65 | self.vk = vk 66 | self.vks = vks 67 | 68 | self.group = PAIRING_GROUP 69 | self.gen = GENERATOR 70 | 71 | def hash_fct(self, msg): 72 | ''' 73 | Hash function used for hashing messages into group G1. 74 | 75 | :param string msg: message to be hashed 76 | :returns: element of G1 group 77 | ''' 78 | 79 | return self.group.hash(msg, G1) 80 | 81 | def lagrange(self, S, i): 82 | ''' 83 | Lagrange interpolation. 84 | 85 | :param list S: list of values for numerator 86 | :param int i: special value for denumerator 87 | ''' 88 | 89 | one = self.group.init(ZR, 1) 90 | S = sorted(S) 91 | num = reduce(lambda x, y: x*y, [0 - j - 1 for j in S if j != i], one) 92 | den = reduce(lambda x, y: x*y, [i - j for j in S if j != i], one) 93 | 94 | return num/den 95 | 96 | def verify_share(self, share, i, msg_hash): 97 | ''' 98 | Verifies if a share generated by i-th party is valid. 99 | 100 | :param int share: share of a signature of a hash of a message 101 | :param int i: index number of a party 102 | :param int msg_hash: hash of a message that is signed 103 | ''' 104 | return pair(share, self.gen) == pair(msg_hash, self.vks[i]) 105 | 106 | def verify_signature(self, signature, msg_hash): 107 | ''' 108 | Verifies if signature is valid. 109 | 110 | :param int signature: signature of msg_hash to be chacked 111 | :param int msg_hash: hash of a message corresponding to signature. 112 | ''' 113 | return pair(signature, self.gen) == pair(msg_hash, self.vk) 114 | 115 | def combine_shares(self, shares): 116 | ''' 117 | Combines shares into a signature of a message. 118 | 119 | :param dict shares: shares of a signature to be produced 120 | ''' 121 | assert len(shares) == self.threshold 122 | R = shares.keys() 123 | return reduce(lambda x,y: x*y, [share ** self.lagrange(R, i) for i, share in shares.items()], 1) 124 | 125 | def hash_msg(self, msg): 126 | ''' 127 | Hashes a message before signing. 128 | 129 | :param bytes msg: message to be hashed 130 | ''' 131 | return self.hash_fct(msg) 132 | 133 | 134 | class SecretKey: 135 | ''' 136 | An object used for generating shares of a signature of a message. 137 | 138 | :param int sk: secret used for signing 139 | ''' 140 | 141 | def __init__(self, sk): 142 | 143 | self.sk = sk 144 | 145 | def generate_share(self, msg_hash): 146 | ''' 147 | Generates a share of a signature of a hash of a message. 148 | 149 | :param int msg_hash: hash of a message which signature share is generated 150 | ''' 151 | return msg_hash ** self.sk 152 | 153 | def _poly(coefs, x): 154 | ''' 155 | Evaluates a polynomial given by coefficients at some point. 156 | 157 | :param list coefs: list of coefficients 158 | :param int x: evaluation point 159 | ''' 160 | return reduce(lambda y, coef: x*y+coef, coefs, 0) 161 | -------------------------------------------------------------------------------- /aleph/data_structures/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | ''' 15 | Data structures used in the protocol. 16 | ''' 17 | 18 | from .poset import Poset 19 | from .userDB import UserDB 20 | from .unit import Unit, pretty_hash 21 | from .tx import Tx 22 | -------------------------------------------------------------------------------- /aleph/data_structures/tx.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | class Tx(object): 15 | '''A class representing a single transaction, that is an act of sending some number of tokens from one user to another.''' 16 | ''' 17 | This class stores a transactions issued by some user 18 | 19 | :param str issuer: public key of the issuer of the transaction 20 | :param str receiver: public key of the receiver of the transaction 21 | :param int amount: amount to be sent to the receiver 22 | ''' 23 | 24 | __slots__ = ['issuer', 'receiver', 'amount'] 25 | 26 | def __init__(self, issuer, receiver, amount): 27 | self.issuer = issuer 28 | self.receiver = receiver 29 | self.amount = amount 30 | 31 | 32 | def __getstate__(self): 33 | return (self.issuer, self.receiver, self.amount) 34 | 35 | 36 | def __setstate__(self, state): 37 | self.issuer, self.receiver, self.amount = state 38 | 39 | 40 | def __str__(self): 41 | tx_string = 'Issuer: ' + str(self.issuer) + '\n' 42 | tx_string += 'Receiver: ' + str(self.receiver) + '\n' 43 | tx_string += 'Amount: ' + str(self.amount) + '\n' 44 | return tx_string 45 | 46 | __repr__ = __str__ 47 | 48 | 49 | def __eq__(self, other): 50 | return (isinstance(other, Tx) and self.issuer == other.issuer 51 | and self.receiver == other.receiver 52 | and self.amount == other.amount) 53 | 54 | 55 | def __hash__(self): 56 | return hash(str(self)) 57 | -------------------------------------------------------------------------------- /aleph/data_structures/userDB.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import logging 15 | 16 | 17 | class UserDB: 18 | ''' 19 | This class is used to store information about user accounts: their balances and last succesful transactions. 20 | ''' 21 | 22 | 23 | def __init__(self, initial_balances_and_indices = []): 24 | ''' 25 | Creates a user data base that contains user balances (by public key) and their last validated transaction. 26 | :param list initial_balances_and_indices: a list of triples consisting of a user's public key, their intial balance and the index of the last transaction performed by them 27 | ''' 28 | self.user_balance = {} 29 | self.user_last_transaction_index = {} 30 | for public_key, balance, index in initial_balances_and_indices: 31 | self.user_balance[public_key] = balance 32 | self.user_last_transaction_index[public_key] = index 33 | 34 | 35 | def account_balance(self, user_public_key): 36 | ''' 37 | Get the balance of the given user. 38 | :param str user_public_key: the public key of the user 39 | ''' 40 | return self.user_balance.get(user_public_key, 0) 41 | 42 | 43 | def last_transaction(self, user_public_key): 44 | ''' 45 | Get the index of the last transaction issued by the given user. 46 | :param str user_public_key: the public key of the user 47 | ''' 48 | return self.user_last_transaction_index.get(user_public_key, -1) 49 | 50 | 51 | def check_transaction_correctness(self, tx): 52 | ''' 53 | Check the correctness of a given transaction. 54 | :param Tx tx: the transaction to check 55 | :returns: True if the transaction has index one higher than the last transaction made by its issuer and the balance allows for the transaction, False otherwise 56 | ''' 57 | issuer_balance = self.user_balance.get(tx.issuer, 0) 58 | issuer_last_transaction = self.user_last_transaction_index.get(tx.issuer, -1) 59 | return tx.amount >= 0 and issuer_balance >= tx.amount and tx.index == issuer_last_transaction + 1 60 | 61 | 62 | def apply_transaction(self, tx): 63 | ''' 64 | Performs the given transaction if it is valid. 65 | :param Tx tx: the transaction to perform 66 | ''' 67 | if self.check_transaction_correctness(tx): 68 | self.user_balance[tx.issuer] -= tx.amount 69 | self.user_balance[tx.receiver] += tx.amount 70 | self.user_last_transaction_index[tx.issuer] += 1 71 | 72 | -------------------------------------------------------------------------------- /aleph/log_analyzer/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | ''' 15 | Tools for extracting statistics from logs. 16 | ''' 17 | 18 | from .log_analyzer import LogAnalyzer 19 | -------------------------------------------------------------------------------- /aleph/log_analyzer/run_analyzer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.log_analyzer import LogAnalyzer 15 | import os 16 | import sys 17 | 18 | def prepare_common_stats(process_stats, rep_dir): 19 | ''' 20 | Write basic stats common to all processes to a file. 21 | ''' 22 | # NOTE: the txps in this stat is calculated as the ratio: 23 | # (total n of txs validated by timing units up to lvl L) / (time to find the timing unit at lvl L) 24 | # where L is the highest level for which a timing unit has been established 25 | 26 | n_stats = len(process_stats) 27 | rep_path = os.path.join(rep_dir, "common-stats.txt") 28 | with open(rep_path, "w") as rep_file: 29 | 30 | fields = ['latency', 'txps'] 31 | header = ''.join( s.ljust(20) for s in ['name', 'median', 'min (proc_id)', 'max (proc_id)']) 32 | print(header) 33 | rep_file.write(header + '\n') 34 | for field in fields: 35 | process_stats.sort(key = lambda x: x[field]) 36 | line = field.ljust(20) 37 | median = process_stats[n_stats//2][field] 38 | line += (f'{median:.3f}').ljust(20) 39 | min_val, min_proc = process_stats[0][field], process_stats[0]['process_id'] 40 | line += (f'{min_val:.3f} ({min_proc})').ljust(20) 41 | max_val, max_proc = process_stats[-1][field], process_stats[-1]['process_id'] 42 | line += (f'{max_val:.3f} ({max_proc})').ljust(20) 43 | print(line) 44 | rep_file.write(line + '\n') 45 | 46 | 47 | def print_help(): 48 | print( "Use one of:\n" 49 | "1) python run_analyzer.py ALL logs_dir reports_dir\n" 50 | " Analyzes all logs in logs_dir and writes report to reports_dir\n" 51 | " If reports_dir is not provided it uses reports_dir = logs_dir\n" 52 | "2) python run_analyzer.py log_file [process_id]\n" 53 | " Analyzes the log_file using process_id (optional)\n" 54 | " Providing process_id is mandatory if the log is shared by multiple processes\n" 55 | ) 56 | 57 | 58 | def analyze_one_log(): 59 | path = sys.argv[1] 60 | if len(sys.argv) == 3: 61 | process_id = int(sys.argv[2]) 62 | else: 63 | print('No process id provided -- assuming that the log comes from one process only.') 64 | process_id = None 65 | 66 | analyzer = LogAnalyzer(path, process_id, generate_plots = False) 67 | if not analyzer.analyze(): 68 | print('Failed because the log does not even contain the Process start message.') 69 | sys.exit(0) 70 | process_id = analyzer.process_id 71 | analyzer.prepare_basic_report('.') 72 | analyzer.prepare_report_per_process('.') 73 | 74 | 75 | def analyze_all_dir(): 76 | log_dir = sys.argv[2] 77 | if len(sys.argv) > 3: 78 | rep_dir = sys.argv[3] 79 | same_dir = False 80 | else: 81 | rep_dir = log_dir 82 | same_dir = True 83 | 84 | if not os.path.isdir(log_dir): 85 | print(f"No such directory {log_dir}.") 86 | sys.exit(0) 87 | 88 | print("Entering.", log_dir) 89 | 90 | if not os.path.isdir(rep_dir): 91 | print(f"No such directory {rep_dir}. Creating.") 92 | os.makedirs(rep_dir, exist_ok=True) 93 | 94 | if same_dir and os.path.isdir(os.path.join(rep_dir, 'txt-basic')): 95 | print("Already analyzed. Skipping.") 96 | return 97 | 98 | list_logs = os.listdir(log_dir) 99 | # do not parse other.log etc. 100 | list_logs = sorted([log_file for log_file in list_logs if os.path.basename(log_file).find("aleph") != -1]) 101 | process_stats = [] 102 | for ind, log_name in enumerate(list_logs): 103 | 104 | path = os.path.join(log_dir, log_name) 105 | print(f'Analyzing {path}...') 106 | if ind == 0: 107 | generate_plots = True 108 | print('Will generate plots only for this log file.') 109 | else: 110 | generate_plots = False 111 | 112 | analyzer = LogAnalyzer(path, generate_plots = generate_plots) 113 | if not analyzer.analyze(): 114 | print('Failed because the log does not even contain the Process start message.') 115 | continue 116 | process_id = analyzer.process_id 117 | print(f"{ind}: Process' {process_id} log analyzed.\n") 118 | analyzer.prepare_basic_report(rep_dir) 119 | analyzer.prepare_report_per_process(rep_dir) 120 | 121 | stats = {'process_id' : process_id} 122 | stats['latency'] = analyzer.get_unit_latency() 123 | stats['txps'] = analyzer.get_txps_till_last_timing_unit() 124 | process_stats.append(stats) 125 | 126 | prepare_common_stats(process_stats, rep_dir) 127 | 128 | 129 | 130 | def parse_args_and_run(): 131 | if len(sys.argv) >= 3 and sys.argv[1] == 'ALL': 132 | analyze_all_dir() 133 | elif len(sys.argv) in [2,3]: 134 | analyze_one_log() 135 | else: 136 | print_help() 137 | 138 | 139 | if __name__ == '__main__': 140 | parse_args_and_run() 141 | 142 | -------------------------------------------------------------------------------- /aleph/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import asyncio 15 | import logging 16 | import multiprocessing 17 | import random 18 | import sys 19 | 20 | from optparse import OptionParser 21 | 22 | from aleph.crypto.keys import SigningKey, VerifyKey 23 | from aleph.data_structures import UserDB, Tx 24 | from aleph.network import tx_listener, tx_source_gen 25 | from aleph.process import Process 26 | 27 | import aleph.const as consts 28 | 29 | 30 | def _read_ip_addresses(ip_addresses_path): 31 | with open(ip_addresses_path, 'r') as f: 32 | return [line[:-1] for line in f] 33 | 34 | 35 | def _read_signing_keys(signing_keys_path): 36 | with open(signing_keys_path, 'r') as f: 37 | hexes = [line[:-1].encode() for line in f] 38 | return [SigningKey(hexed) for hexed in hexes] 39 | 40 | 41 | def _sort_and_get_my_pid(public_keys, signing_keys, my_ip, ip_addresses): 42 | ind = ip_addresses.index(my_ip) 43 | my_pk = public_keys[ind] 44 | 45 | pk_hexes = [pk.to_hex() for pk in public_keys] 46 | arg_sort = [i for i, _ in sorted(enumerate(pk_hexes), key = lambda x: x[1])] 47 | public_keys = [public_keys[i] for i in arg_sort] 48 | signing_keys = [signing_keys[i] for i in arg_sort] 49 | ip_addresses = [ip_addresses[i] for i in arg_sort] 50 | 51 | return public_keys.index(my_pk), public_keys, signing_keys, ip_addresses 52 | 53 | 54 | def _log_consts(): 55 | logger = logging.getLogger(consts.LOGGER_NAME) 56 | consts_names = ['N_PARENTS', 'USE_TCOIN', 'CREATE_DELAY', 'SYNC_INIT_DELAY', 'TXPU', 'LEVEL_LIMIT', 'UNITS_LIMIT'] 57 | consts_values = [] 58 | for const_name in consts_names: 59 | consts_values.append(f'{const_name}={consts.__dict__[const_name]}') 60 | logger.info('; '.join(consts_values)) 61 | 62 | 63 | async def main(): 64 | ''' 65 | A task to run as a single member of the Aleph committee. 66 | ''' 67 | _log_consts() 68 | 69 | signing_keys = _read_signing_keys('signing_keys') 70 | ip_addresses = _read_ip_addresses('ip_addresses') 71 | with open('my_ip', 'r') as f: 72 | my_ip = f.readline().strip() 73 | 74 | assert len(ip_addresses) == len(signing_keys), 'number of hosts and signing keys dont match!!!' 75 | public_keys = [VerifyKey.from_SigningKey(sk) for sk in signing_keys] 76 | 77 | process_id, public_keys, signing_keys, ip_addresses = _sort_and_get_my_pid(public_keys, signing_keys, my_ip, ip_addresses) 78 | addresses = [(ip, consts.HOST_PORT) for ip in ip_addresses] 79 | 80 | sk, pk = signing_keys[process_id], public_keys[process_id] 81 | 82 | n_processes = len(ip_addresses) 83 | userDB = None 84 | 85 | recv_address = None 86 | if consts.TX_SOURCE == 'tx_source_gen': 87 | tx_source = tx_source_gen(consts.TX_LIMIT, consts.TXPU, process_id) 88 | else: 89 | tx_source = tx_listener 90 | 91 | process = Process(n_processes, 92 | process_id, 93 | sk, pk, 94 | addresses, 95 | public_keys, 96 | recv_address, 97 | userDB, 98 | tx_source) 99 | 100 | await process.run() 101 | 102 | 103 | if __name__ == '__main__': 104 | asyncio.run(main()) 105 | -------------------------------------------------------------------------------- /aleph/network/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from .network import Network 15 | from .tx_traffic import tx_listener, tx_source_gen, tx_generator 16 | -------------------------------------------------------------------------------- /aleph/network/channel.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import asyncio 15 | import logging 16 | 17 | import aleph.const as consts 18 | 19 | 20 | class RejectException(Exception): 21 | pass 22 | 23 | 24 | class Channel: 25 | ''' Simple class representing an asynchronous communication channel through the network. Suitable for both a case when I am initiating connection (see open()) or when someone else does that (see connect()). 26 | 27 | Class representing an asynchronous communication channel through the network. Suitable for both a case when we are 28 | initiating a connection (see open()) or when someone else does that (see connect()). 29 | :param int owner_id: process ID of the owner of the channel 30 | :param int peer_id: process ID of the recipient (other end) of the channel 31 | :param tuple peer_address: pair (IP, port) with peer's address 32 | 33 | ''' 34 | 35 | REJECT = b'REJECT' 36 | 37 | def __init__(self, owner_id, peer_id, peer_address): 38 | self.owner_id = owner_id 39 | self.peer_id = peer_id 40 | self.address = peer_address 41 | self.active = asyncio.Event() 42 | self.in_use = asyncio.Lock() 43 | self.reader = None 44 | self.writer = None 45 | 46 | @staticmethod 47 | async def receive_handshake(reader, writer): 48 | '''Receive handshake from an unknown process and find out their process_id.''' 49 | 50 | data = await reader.readuntil() 51 | return int(data.rstrip(b'\n')) 52 | 53 | def send_handshake(self): 54 | '''Introduce yourself (send process_id) to newly connected process.''' 55 | 56 | self.writer.write(f'{self.owner_id}\n'.encode()) 57 | 58 | def connect(self, reader, writer): 59 | '''Activate channel by connecting existing reader and writer to it.''' 60 | 61 | self.reader = reader 62 | self.writer = writer 63 | self.active.set() 64 | 65 | def is_active(self): 66 | '''Guess what...''' 67 | 68 | return self.active.is_set() 69 | 70 | async def reject(self): 71 | '''Send REJECT message.''' 72 | 73 | if self.is_active(): 74 | self.writer.write(self.REJECT) 75 | self.writer.write(b'\n') 76 | await self.writer.drain() 77 | 78 | async def read(self): 79 | ''' 80 | Read data from the channel. 81 | If channel has not been activated yet, block and wait. 82 | If obtained REJECT message, raise RejectException. 83 | ''' 84 | 85 | await self.active.wait() 86 | 87 | data = await self.reader.readuntil() 88 | data = data.rstrip(b'\n') 89 | if data == self.REJECT: 90 | raise RejectException() 91 | n_bytes = int(data) 92 | data = await self.reader.readexactly(n_bytes) 93 | return data 94 | 95 | async def write(self, data): 96 | '''Send data through the channel.''' 97 | 98 | if not self.is_active(): 99 | await self.open() 100 | 101 | self.writer.write(str(len(data)).encode()) 102 | self.writer.write(b'\n') 103 | self.writer.write(data) 104 | await self.writer.drain() 105 | 106 | async def open(self): 107 | '''Activate the channel by opening a new connection to the peer.''' 108 | 109 | logger = logging.getLogger(consts.LOGGER_NAME) 110 | logger.info(f'sync_open_chan {self.owner_id} | Opening connection to {self.peer_id} - start') 111 | while True: 112 | fut = asyncio.open_connection(*self.address) 113 | try: 114 | self.reader, self.writer = await asyncio.wait_for(fut, timeout=1) 115 | break 116 | except (asyncio.TimeoutError, ConnectionRefusedError): 117 | logger.info(f'sync_open_chan {self.owner_id} | Opening connection to {self.peer_id} - failed') 118 | await asyncio.sleep(1) 119 | 120 | logger.info(f'sync_open_chan {self.owner_id} | Opening connection to {self.peer_id} - succeded') 121 | 122 | self.send_handshake() 123 | self.active.set() 124 | 125 | async def close(self): 126 | '''Close the channel (unused for now).''' 127 | 128 | if self.is_active(): 129 | self.writer.close() 130 | await self.writer.wait_closed() 131 | self.reader, self.writer = None, None 132 | self.active.clear() 133 | -------------------------------------------------------------------------------- /aleph/network/tx_traffic.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import logging 15 | import pickle 16 | import pkg_resources 17 | import random 18 | import socket 19 | import socketserver 20 | 21 | from time import sleep, perf_counter as get_time 22 | 23 | from aleph.data_structures import Tx 24 | import aleph.const as consts 25 | 26 | 27 | def tx_listener(listen_addr, queue): 28 | ''' 29 | Start a TCP server on *listen_addr* and listen for incoming transactions. 30 | Put batches (lists) of transactions on *queue* every consts.CREATE_DELAY seconds or when the tx limit per unit (consts.TXPU) 31 | is reached, whichever comes first. 32 | ''' 33 | tx_buffer = [] 34 | prev_put_time = get_time() 35 | 36 | class TCPHandler(socketserver.BaseRequestHandler): 37 | def handle(self): 38 | nonlocal tx_buffer, prev_put_time 39 | logger.info(f'tx_server_establish | Connection with {self.client_address}') 40 | 41 | data = self.request.recv(1024) 42 | tx = pickle.loads(data) 43 | tx_buffer.append(tx) 44 | 45 | logger.info(f'tx_server_receive | Received from {self.client_address}') 46 | 47 | if len(tx_buffer) == consts.TXPU or get_time() - prev_put_time > consts.CREATE_DELAY: 48 | prev_put_time = get_time() 49 | logger.info(f'tx_server_enqueue | Putting {len(tx_buffer)} txs on queue') 50 | queue.put(tx_buffer) 51 | tx_buffer = [] 52 | 53 | logger = logging.getLogger(consts.LOGGER_NAME) 54 | logger.info(f'tx_server_start | Starting on {listen_addr}') 55 | 56 | with socketserver.TCPServer(listen_addr, TCPHandler) as server: 57 | server.serve_forever() 58 | 59 | 60 | def tx_source_gen(batch_size, txpu, seed=27091986, filename=None): 61 | ''' 62 | Produces a simple tx generator. 63 | :param int batch_size: number of txs for a process to input into the system. 64 | :param int txpu: number of txs to be included in one unit. 65 | :param int seed: seed for random generator. 66 | :param str filename: path to file with names of txs senders and recipients (each in a separate line). 67 | If None, aleph/test/data/light_nodes_public_keys is used. 68 | ''' 69 | 70 | if filename is None: 71 | filename = pkg_resources.resource_stream('aleph.test.data', 'light_nodes_public_keys') 72 | lines = [line.decode() for line in filename.readlines()] 73 | else: 74 | with open(filename) as f: 75 | lines = f.readlines() 76 | ln_public_keys = [line.rstrip('\n') for line in lines] 77 | 78 | def _tx_source(dummy, queue): 79 | ''' 80 | Generates transactions in bundles of size txpu till batch_size is reached 81 | :param dummy: dummy argument needed for comatibility of args list with tx_listener() 82 | :param queue queue: queue for newly generated txs 83 | ''' 84 | # ensure that batches are different 85 | random.seed(seed) 86 | 87 | produced = 0 88 | while produced < batch_size: 89 | offset = min(txpu, batch_size - produced) 90 | 91 | txs = [] 92 | for _ in range(offset): 93 | source, target = random.sample(ln_public_keys, k=2) 94 | amount = random.randint(1, 30000) 95 | txs.append(Tx(source, target, amount)) 96 | 97 | produced += offset 98 | queue.put(txs, block=True) 99 | 100 | return _tx_source 101 | 102 | 103 | def tx_generator(committee_addresses, signing_keys, txps): 104 | ''' 105 | Generate random transactions indefinitely. 106 | Issuer and receiver of each transactions are random integers 0 <= i <= len(signing_keys) 107 | 108 | NOTE: this is an old version of TX generator that is still used by some tests. 109 | ''' 110 | n_light_nodes = len(signing_keys) 111 | counter = 0 112 | starts = get_time() 113 | 114 | while True: 115 | if counter == txps: 116 | counter = 0 117 | delta = get_time() - starts 118 | if delta < 1: 119 | sleep(1 - delta) 120 | starts = get_time() 121 | 122 | issuer_id = random.randrange(0, n_light_nodes) 123 | receiver_id = random.choice([uid for uid in range(n_light_nodes) if uid != issuer_id]) 124 | amount = random.randrange(1, 100) 125 | tx = Tx(issuer_id, receiver_id, amount) 126 | data = pickle.dumps(tx) 127 | 128 | sent = False 129 | while not sent: 130 | com_addr = random.choice(committee_addresses) 131 | 132 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: 133 | try: 134 | sock.connect(com_addr) 135 | sock.sendall(data) 136 | sent = True 137 | except: 138 | # assume any failure means that others have stopped 139 | return 140 | 141 | counter += 1 142 | -------------------------------------------------------------------------------- /aleph/test/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | -------------------------------------------------------------------------------- /aleph/test/data/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | -------------------------------------------------------------------------------- /aleph/test/data/simple.dag: -------------------------------------------------------------------------------- 1 | format standard 2 | 4 3 | 0- 0 4 | 1- 1 5 | 2- 2 6 | 1-R 1 1- 2- 7 | 2-J 2 2- 1-R 8 | 3- 3 9 | 3-M 3 3- 1-R 10 | 1-Z 1 1-R 3-M 11 | 2-R 2 2-J 1-Z 12 | 1-I 1 1-Z 2-R 13 | 0-X 0 0- 1-I 14 | 2-A 2 2-R 1-I 15 | 3-V 3 3-M 2-R 16 | 1-X 1 1-I 3-V 17 | 2-S 2 2-A 1-X 18 | 1-V 1 1-X 2-S 19 | 3-J 3 3-V 1-X 20 | 2-D 2 2-S 3-J 21 | 3-R 3 3-J 2-D 22 | 1-T 1 1-V 3-R 23 | 0-O 0 0-X 1-T 24 | 2-O 2 2-D 1-T 25 | 1-J 1 1-T 2-O 26 | 3-X 3 3-R 1-T 27 | 2-B 2 2-O 3-X 28 | 3-L 3 3-X 2-B 29 | 1-VG 1 1-J 3-L 30 | 2-L 2 2-B 1-VG 31 | 3-K 3 3-L 2-L 32 | 1-Q 1 1-VG 2-L 33 | -------------------------------------------------------------------------------- /aleph/test/test_message_signing.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import random 15 | import string 16 | 17 | from aleph.crypto import SigningKey, VerifyKey 18 | 19 | def test_true(): 20 | ''' 21 | Test whether correct message signatures are accepted. 22 | ''' 23 | sk = SigningKey() 24 | vk = VerifyKey.from_SigningKey(sk) 25 | for _ in range(10): 26 | n = random.randint(50,200) 27 | msg = ''.join(random.choices(string.printable, k=n)) 28 | if random.randint(0,1): 29 | msg = msg.encode() 30 | sign = sk.sign(msg) 31 | assert vk.verify_signature(sign, msg) 32 | 33 | 34 | def test_false(): 35 | ''' 36 | Test whether incorrect message signatures are rejected. 37 | ''' 38 | sk = SigningKey() 39 | vk = VerifyKey.from_SigningKey(sk) 40 | for _ in range(10): 41 | n = random.randint(50,200) 42 | msg = ''.join(random.choices(string.printable, k=n)) 43 | if random.randint(0,1): 44 | msg = msg.encode() 45 | sign = sk.sign(msg) 46 | # drop one random character in the message to produce a different message 47 | k = random.randint(0, n-1) 48 | msg = msg[:k] + msg[k+1:] 49 | assert not vk.verify_signature(sign, msg) 50 | -------------------------------------------------------------------------------- /aleph/test/test_poset_below.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.data_structures import Unit, Poset 15 | from aleph.utils import dag_utils 16 | from aleph.utils.generic_test import generate_and_check_dag 17 | import random 18 | 19 | 20 | def test_trivial_single_level_below(): 21 | ''' 22 | A simple manual test that makes sure that poset.max_units_per_process are correctly set when only dealing units are present 23 | and that after adding one additional unit to the poset, Poset.below returns correct results. 24 | ''' 25 | 26 | # start by creating a poset with 4 processes and add the 4 dealing units to it 27 | n_processes = 4 28 | poset = Poset(n_processes = n_processes, use_tcoin = False) 29 | dealing_units_per_process = [Unit(creator_id = i, parents = [], txs = []) for i in range(n_processes)] 30 | for i in range(n_processes): 31 | poset.prepare_unit(dealing_units_per_process[i]) 32 | poset.add_unit(dealing_units_per_process[i]) 33 | 34 | # make sure that dealing units are set as maximal units in the poset 35 | for i in range(n_processes): 36 | assert poset.max_units_per_process[i][0] is dealing_units_per_process[i] 37 | 38 | U0 = poset.max_units_per_process[0][0] 39 | U1 = poset.max_units_per_process[1][0] 40 | U2 = poset.max_units_per_process[2][0] 41 | U3 = poset.max_units_per_process[3][0] 42 | 43 | # add one new unit with parents U0, U1 to the poset 44 | U = Unit(creator_id = 0, parents = [U0, U1], txs = []) 45 | poset.prepare_unit(U) 46 | poset.add_unit(U) 47 | 48 | assert poset.below(U0, U) 49 | assert poset.above(U, U0) 50 | assert poset.below(U1, U) 51 | assert not poset.below(U2, U) 52 | assert not poset.below(U3, U) 53 | 54 | 55 | def test_small_nonforking_below(): 56 | generate_and_check_dag( 57 | checks= [check_all_pairs_below], 58 | n_processes = 5, 59 | n_units = 50, 60 | repetitions = 30, 61 | ) 62 | 63 | 64 | def test_large_nonforking_below(): 65 | generate_and_check_dag( 66 | checks= [check_all_pairs_below], 67 | n_processes = 100, 68 | n_units = 200, 69 | repetitions = 1, 70 | ) 71 | 72 | 73 | def test_small_forking_below(): 74 | n_processes = 5 75 | generate_and_check_dag( 76 | checks= [check_all_pairs_below], 77 | n_processes = n_processes, 78 | n_units = 50, 79 | repetitions = 30, 80 | forking = lambda: random.randint(0, n_processes) 81 | ) 82 | 83 | def test_large_forking_below(): 84 | n_processes = 100 85 | generate_and_check_dag( 86 | checks= [check_all_pairs_below], 87 | n_processes = n_processes, 88 | n_units = 200, 89 | repetitions = 1, 90 | forking = lambda: random.randint(0, n_processes) 91 | ) 92 | 93 | 94 | def check_all_pairs_below(dag): 95 | ''' 96 | Create a poset from a dag and test (U <= V) for all pairs of units U, V against the implementation in the DAG class. 97 | ''' 98 | poset, unit_dict = dag_utils.poset_from_dag(dag) 99 | 100 | for nodeU, U in unit_dict.items(): 101 | for nodeV, V in unit_dict.items(): 102 | assert poset.below(U,V) == dag.is_reachable(nodeU, nodeV), f"Problem with {nodeU} and {nodeV}" 103 | 104 | -------------------------------------------------------------------------------- /aleph/test/test_poset_break_ties.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils import dag_utils 15 | from aleph.utils.generic_test import generate_and_check_dag 16 | import random 17 | from itertools import combinations 18 | 19 | 20 | def lower_cone(poset, U): 21 | ''' 22 | :param Poset poset: the considered poset 23 | :param Unit U: the unit whose lower cone is to be computed 24 | :returns: The set of all units that are below U in poset. 25 | ''' 26 | ret = set([U]) 27 | for P in U.parents: 28 | ret |= lower_cone(poset,P) 29 | return ret 30 | 31 | 32 | def check_total_order_vs_below(poset, units): 33 | ''' 34 | Tests whether the linear ordering output by break_ties is compatible with the poset. 35 | :param Poset poset: the considered poset 36 | :param list units: a list of units to be tested 37 | ''' 38 | total_order = poset.break_ties(units) 39 | for U,V in combinations(total_order, 2): 40 | assert not poset.above(U,V) 41 | 42 | 43 | def check_total_order_invariance(poset, units, repetitions=3): 44 | ''' 45 | Tests whether the linear ordering output by break_ties does not depend on the initial ordering of the input. 46 | :param Poset poset: the considered poset 47 | :param list units: a list of units to be tested 48 | :param int repetitions: the number of tests to perform 49 | ''' 50 | total_order = poset.break_ties(units) 51 | for _ in range(repetitions): 52 | random.shuffle(units) 53 | test = poset.break_ties(units) 54 | assert total_order == test 55 | 56 | 57 | def check_break_ties_for_units(poset, units): 58 | ''' 59 | Run the correctness tests of break ties on a given poset for a given set of units. 60 | :param Poset poset: the considered poset 61 | :param list units: a list of units to be tested 62 | ''' 63 | check_total_order_vs_below(poset, units) 64 | check_total_order_invariance(poset, units) 65 | 66 | 67 | def check_break_ties(dag): 68 | ''' 69 | Takes a dag as input, turns it into a poset and runs tests for break ties: 70 | - on all units, 71 | - on the lower-cone of a random unit. 72 | :param DAG dag: a dag to run the test 73 | ''' 74 | poset, __ = dag_utils.poset_from_dag(dag) 75 | check_break_ties_for_units(poset, list(poset.units.values())) 76 | random_unit = random.choice(list(poset.units.values())) 77 | check_break_ties_for_units(poset, list(lower_cone(poset, random_unit))) 78 | 79 | 80 | def test_small_nonforking_break_ties(): 81 | generate_and_check_dag( 82 | checks= [check_break_ties], 83 | n_processes = 5, 84 | n_units = 50, 85 | repetitions = 20, 86 | ) 87 | 88 | 89 | def test_large_nonforking_break_ties(): 90 | generate_and_check_dag( 91 | checks= [check_break_ties], 92 | n_processes = 30, 93 | n_units = 500, 94 | repetitions = 1, 95 | ) 96 | 97 | 98 | def test_large_forking_break_ties(): 99 | generate_and_check_dag( 100 | checks= [check_break_ties], 101 | n_processes = 30, 102 | n_units = 500, 103 | repetitions = 1, 104 | forking = lambda: 3 105 | ) 106 | -------------------------------------------------------------------------------- /aleph/test/test_poset_compliance.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.data_structures import Unit, Poset 15 | from aleph.utils import dag_utils 16 | import random 17 | import pytest 18 | 19 | 20 | def check_compliance_vs_pattern(dag, topological_list, pattern, compliance_rules = None): 21 | ''' 22 | Create a poset from dag and make sure that whenever a new unit is added, the answer to the check_compliance test agrees with 23 | a precomputed set of answers: a pattern. 24 | :param DAG dag: the dag against which we want to test the check_compliance procedure 25 | :param list topological_list: a list of nodes in dag in a topological order 26 | :param list pattern: the list of answers (True, False) to the compliance test for the subsequent nodes in topological_list 27 | :param dict compliance_rules: which compliance rule should be activated in the poset 28 | ''' 29 | unit_dict = {} 30 | poset = Poset(n_processes = dag.n_processes, compliance_rules = compliance_rules, use_tcoin = False) 31 | 32 | for node, answer in zip(topological_list, pattern): 33 | U = Unit(creator_id = dag.pid(node), parents = [unit_dict[parent] for parent in dag.parents(node)], txs = []) 34 | unit_dict[node] = U 35 | poset.prepare_unit(U) 36 | assert poset.check_compliance(U) == answer, f"Node {node} was problematic." 37 | poset.add_unit(U) 38 | 39 | 40 | 41 | def test_small_random_compliance(): 42 | ''' 43 | Generates dags that and uses them to test the check_compliance method (with standard compliance rules) in poset. 44 | Every dag is generated as follows: 45 | 1) phase 1: generate a certain number of units making sure that all of them comply to the rules 46 | 2) phase 2: generate units one by one until we find one that *does not* comply to the rules (in a prespecified way) 47 | Thus the last unit should be rejected by check compliance, whereas the remaining ones should be accepted. 48 | ''' 49 | random.seed(123456789) 50 | repetitions = 800 51 | properties = ['expand_primes'] 52 | for violated_property in properties: 53 | for rep in range(repetitions): 54 | n_processes = random.randint(4, 5) 55 | n_units = random.randint(0, n_processes*2) 56 | if violated_property == 'forker_muting': 57 | n_forkers = random.randint(1, n_processes) 58 | else: 59 | n_forkers = random.randint(0, n_processes) 60 | constraints_ensured = {property : True for property in properties} 61 | constraints_ensured['distinct_parents'] = True 62 | constraints_ensured['forker_muting'] = True 63 | constraints_violated = {violated_property : False} 64 | constraints_violated['distinct_parents'] = True 65 | constraints_violated['forker_muting'] = True 66 | dag, topological_list = dag_utils.generate_random_violation(n_processes, n_units, n_forkers, 67 | constraints_ensured, constraints_violated) 68 | # construct the pattern: only the last unit should fail the compliance test 69 | pattern = [True] * len(topological_list) 70 | pattern[-1] = False 71 | check_compliance_vs_pattern(dag, topological_list, pattern) 72 | 73 | 74 | 75 | def test_large_random_compliance(): 76 | ''' 77 | The same as test_small_random_compliance() but tests larger posets. 78 | ''' 79 | random.seed(123456789) 80 | repetitions = 20 81 | properties = ['expand_primes'] 82 | for violated_property in properties: 83 | for rep in range(repetitions): 84 | n_processes = random.randint(30, 80) 85 | n_units = random.randint(0, n_processes*2) 86 | if violated_property == 'forker_muting': 87 | n_forkers = random.randint(1, n_processes//3) 88 | else: 89 | n_forkers = random.randint(0, n_processes//3) 90 | constraints_ensured = {property : True for property in properties} 91 | constraints_ensured['distinct_parents'] = True 92 | constraints_ensured['forker_muting'] = True 93 | constraints_violated = {violated_property : False} 94 | constraints_violated['distinct_parents'] = True 95 | constraints_violated['forker_muting'] = True 96 | dag, topological_list = dag_utils.generate_random_violation(n_processes, n_units, n_forkers, 97 | constraints_ensured, constraints_violated) 98 | pattern = [True] * len(topological_list) 99 | pattern[-1] = False 100 | check_compliance_vs_pattern(dag, topological_list, pattern) 101 | -------------------------------------------------------------------------------- /aleph/test/test_poset_create_unit.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils.generic_test import simulate_with_checks 15 | import random 16 | 17 | def test_create_unit_small(): 18 | random.seed(123456789) 19 | repetitions = 50 20 | for rep in range(repetitions): 21 | n_processes = random.randint(4, 15) 22 | n_units = random.randint(0, n_processes*5) 23 | simulate_with_checks( 24 | n_processes, 25 | n_units, 26 | ) 27 | 28 | 29 | def test_create_unit_large(): 30 | random.seed(123456789) 31 | repetitions = 5 32 | for rep in range(repetitions): 33 | n_processes = random.randint(30, 80) 34 | n_units = random.randint(0, n_processes*3) 35 | simulate_with_checks( 36 | n_processes, 37 | n_units, 38 | ) 39 | -------------------------------------------------------------------------------- /aleph/test/test_poset_floor.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.data_structures import Unit, Poset 15 | from aleph.utils import dag_utils 16 | from aleph.utils.generic_test import generate_and_check_dag 17 | import random 18 | 19 | def create_poset_foundation(n_processes): 20 | ''' 21 | Creates a layer of n_processes dealing units and adds them to a poset. 22 | :returns: the resulting poset 23 | ''' 24 | poset = Poset(n_processes = n_processes, use_tcoin = False) 25 | empty_floor = [[] for _ in range(n_processes)] 26 | 27 | bottom_units_per_process = [Unit(creator_id = i, parents = [], txs = []) for i in range(n_processes)] 28 | for i in range(n_processes): 29 | poset.prepare_unit(bottom_units_per_process[i]) 30 | poset.add_unit(bottom_units_per_process[i]) 31 | return poset 32 | 33 | def test_trivial_single_level(): 34 | ''' 35 | Tests floors for dealing units. 36 | ''' 37 | n_processes = 4 38 | poset = create_poset_foundation(n_processes) 39 | for i in range(n_processes): 40 | checkedUnit = poset.max_units_per_process[i][0] 41 | for j in range(n_processes): 42 | assert checkedUnit.floor[j] == ([checkedUnit] if j == i else []) 43 | 44 | 45 | def test_simple_tower(): 46 | ''' 47 | A simple manual test for floors. 48 | ''' 49 | n_processes = 4 50 | poset = create_poset_foundation(n_processes) 51 | 52 | foundation_units = [unit[0] for unit in poset.max_units_per_process] 53 | 54 | U01 = Unit(creator_id = 0, parents = [foundation_units[0], foundation_units[1]], txs = []) 55 | poset.prepare_unit(U01) 56 | poset.add_unit(U01) 57 | U02 = Unit(creator_id = 0, parents = [U01, foundation_units[2]], txs = []) 58 | poset.prepare_unit(U02) 59 | poset.add_unit(U02) 60 | U03 = Unit(creator_id = 0, parents = [U02, foundation_units[3]], txs = []) 61 | poset.prepare_unit(U03) 62 | poset.add_unit(U03) 63 | for j in range(n_processes): 64 | assert U03.floor[j] == ([U03] if j == 0 else [foundation_units[j]]) 65 | assert U02.floor == [[U02], [foundation_units[1]], [foundation_units[2]], []] 66 | assert U01.floor == [[U01], [foundation_units[1]], [], []] 67 | for i in range(n_processes): 68 | for j in range(n_processes): 69 | assert foundation_units[i].floor[j] == ([foundation_units[i]] if j == i else []) 70 | 71 | def check_all_floors(dag): 72 | ''' 73 | Given a dag, a poset is generated and the floor of every unit is tested against the implementation in DAG. 74 | ''' 75 | poset, unit_dict = dag_utils.poset_from_dag(dag) 76 | for nodeU, U in unit_dict.items(): 77 | for [tile, other] in zip(U.floor, [[unit_dict[nodeV] for nodeV in nodes] for nodes in dag.floor(nodeU)]): 78 | assert set(tile) == set(other) 79 | 80 | def test_small_nonforking(): 81 | generate_and_check_dag( 82 | checks= [check_all_floors], 83 | n_processes = 5, 84 | n_units = 50, 85 | repetitions = 30, 86 | ) 87 | 88 | def test_large_nonforking(): 89 | generate_and_check_dag( 90 | checks= [check_all_floors], 91 | n_processes = 100, 92 | n_units = 200, 93 | repetitions = 1, 94 | ) 95 | 96 | def test_small_forking(): 97 | n_processes = 5 98 | generate_and_check_dag( 99 | checks= [check_all_floors], 100 | n_processes = n_processes, 101 | n_units = 50, 102 | repetitions = 30, 103 | forking = lambda: random.randint(0, n_processes) 104 | ) 105 | 106 | def test_large_forking(): 107 | n_processes = 100 108 | generate_and_check_dag( 109 | checks= [check_all_floors], 110 | n_processes = n_processes, 111 | n_units = 200, 112 | repetitions = 1, 113 | forking = lambda: random.randint(0, n_processes) 114 | ) 115 | -------------------------------------------------------------------------------- /aleph/test/test_poset_level.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils import dag_utils 15 | from aleph.utils.generic_test import generate_and_check_dag 16 | import random 17 | 18 | 19 | 20 | def test_small_nonforking_below(): 21 | generate_and_check_dag( 22 | checks= [check_all_levels], 23 | n_processes = 5, 24 | n_units = 50, 25 | repetitions = 1, 26 | ) 27 | 28 | def test_large_nonforking_below(): 29 | generate_and_check_dag( 30 | checks= [check_all_levels], 31 | n_processes = 30, 32 | n_units = 100, 33 | repetitions = 1, 34 | ) 35 | 36 | 37 | def test_small_forking_below(): 38 | generate_and_check_dag( 39 | checks= [check_all_levels], 40 | n_processes = 5, 41 | n_units = 50, 42 | repetitions = 30, 43 | forking = lambda: 1 44 | ) 45 | 46 | 47 | def test_large_forking_below(): 48 | generate_and_check_dag( 49 | checks= [check_all_levels], 50 | n_processes = 30, 51 | n_units = 100, 52 | repetitions = 10, 53 | forking = lambda: 2 54 | ) 55 | 56 | def test_specific_dag(): 57 | from pkg_resources import resource_stream 58 | check_all_levels(dag_utils.dag_from_stream(resource_stream("aleph.test.data", "simple.dag"))) 59 | 60 | def check_all_levels(arg): 61 | ''' 62 | Create a poset from a dag and check if levels agree. 63 | ''' 64 | poset, unit_dict = dag_utils.poset_from_dag(arg) 65 | 66 | for nodeU, U in unit_dict.items(): 67 | assert U.level == arg.levels[nodeU], f"Node {nodeU} has broken level!" 68 | -------------------------------------------------------------------------------- /aleph/test/test_poset_threshold_coin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils.generic_test import simulate_with_checks 15 | import aleph.const as consts 16 | import random 17 | 18 | 19 | def toss_for_prime(U, poset, dag, results, additional_args): 20 | ''' 21 | For every prime unit of level>=7 try to toss a coin for some random prime at level>=3 that is >=4 levels below. 22 | ''' 23 | if additional_args is None: 24 | primes = [] 25 | else: 26 | primes = additional_args 27 | if poset.is_prime(U) and U.level>=3: 28 | primes.append(U) 29 | if U.level>=7: 30 | random.shuffle(primes) 31 | # find any prime unit that is >=4 levels below U 32 | for U_c in primes: 33 | if U_c.level<=U.level - 4: 34 | results.append((U.level, poset.toss_coin(U_c, U))) 35 | break 36 | return primes 37 | 38 | 39 | def test_threshold_coin_toss(): 40 | ''' 41 | Test whether the toss_coin code succeeds (i.e. really whether it terminates with no exception). 42 | ''' 43 | n_processes = 6 44 | n_units = 320 45 | results = simulate_with_checks( 46 | n_processes, 47 | n_units, 48 | post_prepare = toss_for_prime, 49 | use_tcoin = True, 50 | seed = 0) 51 | # the poset should be high enough so that toss_for_prime produces some coin tosses 52 | assert len(results) > 0 53 | # we should reach beyond const.ADD_SHARES so that at least one coin toss happens by combining shares and not using simple_coin 54 | assert results[-1][0] > consts.ADD_SHARES, "Too low poset generated" 55 | 56 | 57 | -------------------------------------------------------------------------------- /aleph/test/test_threshold_coin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import random 15 | 16 | from aleph.crypto.threshold_coin import ThresholdCoin 17 | from aleph.crypto.threshold_signatures import generate_keys 18 | 19 | 20 | def test_tcoin(): 21 | ''' 22 | A simple test for generating and extracting threshold coin tosses. 23 | ''' 24 | n_parties, threshold = 10, 5 25 | VK, SKs = generate_keys(n_parties, threshold) 26 | 27 | dealer_id = random.randint(0, n_parties) 28 | TCs = [ThresholdCoin(dealer_id, pid, n_parties, threshold, SK, VK) for pid, SK in enumerate(SKs)] 29 | 30 | nonce = random.randint(0, 100000) 31 | 32 | # generate coin shares of all parties 33 | shares = [TC.create_coin_share(nonce) for TC in TCs] 34 | 35 | # verify all shares 36 | for i, share in enumerate(shares): 37 | pid = random.randrange(n_parties) 38 | assert TCs[pid].verify_coin_share(share, i, nonce) 39 | 40 | _shares = {i: shares[i] for i in random.sample(range(n_parties), threshold)} 41 | 42 | pid = random.randrange(n_parties) 43 | assert TCs[pid].combine_coin_shares(_shares, str(nonce))[0] in [0, 1] 44 | -------------------------------------------------------------------------------- /aleph/test/test_threshold_signatures.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from random import sample 15 | 16 | from aleph.crypto.threshold_signatures import generate_keys 17 | 18 | 19 | def test_combine_signature(): 20 | ''' 21 | Performs the following: 22 | - creates a threshold signature (5 out of 10) for a message. 23 | - verifies all the shares 24 | - combines all the shares into a signature 25 | - verifies the signature 26 | ''' 27 | n_parties, threshold = 10, 5 28 | VK, SKs = generate_keys(n_parties, threshold) 29 | 30 | msg = 'there is no spoon' 31 | msg_hash = VK.hash_msg(msg) 32 | 33 | # generate signature shares of all parties 34 | shares = [SK.generate_share(msg_hash) for SK in SKs] 35 | _shares = {i: shares[i] for i in sample(range(n_parties), threshold)} 36 | 37 | # check if all shares are valid 38 | for i, share in _shares.items(): 39 | assert VK.verify_share(share, i, msg_hash) 40 | 41 | # combine shares and check if the signature is valid 42 | signature = VK.combine_shares(_shares) 43 | 44 | assert VK.verify_signature(signature, msg_hash) 45 | -------------------------------------------------------------------------------- /aleph/test/test_unit_signing.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.crypto.keys import SigningKey, VerifyKey 15 | from aleph.data_structures import Unit 16 | from aleph.process import Process 17 | 18 | 19 | def test_signing(): 20 | ''' 21 | Tests whether a correct signature of a Unit.bytestring() is positively verified. 22 | ''' 23 | sk = SigningKey() 24 | vk = VerifyKey.from_SigningKey(sk) 25 | U = Unit(0,[],[]) 26 | msg = U.bytestring() 27 | signature = sk.sign(msg) 28 | assert vk.verify_signature(signature, msg) 29 | 30 | def test_process_signing(): 31 | ''' 32 | Tests whether a process correctly signs a Unit. 33 | ''' 34 | process_id = 0 35 | n_processes = 100 36 | sk = SigningKey() 37 | vk = VerifyKey.from_SigningKey(sk) 38 | dummy_keys = [VerifyKey.from_SigningKey(SigningKey()) for _ in range(n_processes)] 39 | dummy_keys[0] = vk 40 | dummy_addresses = [(None, None) for _ in range(n_processes)] 41 | process = Process(n_processes, process_id, sk, vk, dummy_addresses, dummy_keys, None) 42 | U = Unit(0,[],[]) 43 | process.sign_unit(U) 44 | 45 | msg = U.bytestring() 46 | assert vk.verify_signature(U.signature, msg) 47 | 48 | -------------------------------------------------------------------------------- /aleph/utils/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from .dag import DAG 15 | from . import dag_utils 16 | from unittest import mock 17 | from .timer import timer 18 | -------------------------------------------------------------------------------- /aleph/utils/plot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import warnings 15 | 16 | with warnings.catch_warnings(): 17 | warnings.filterwarnings("ignore",category=DeprecationWarning) 18 | import networkx as nx 19 | from networkx.drawing.nx_agraph import graphviz_layout 20 | 21 | import numpy as np 22 | import logging 23 | logging.getLogger('matplotlib').setLevel(logging.WARNING) 24 | import matplotlib.pyplot as plt 25 | 26 | from aleph.utils.dag_utils import dag_from_poset 27 | 28 | 29 | 30 | def plot_dag(dag): 31 | ''' 32 | Given a DAG instance dag, generates and shows its plot. 33 | ''' 34 | G = nx.DiGraph() 35 | height, creator = {}, {} 36 | branch = {pid:{} for pid in range(dag.n_processes)} 37 | self_descendant, self_predecessor = {}, {} 38 | 39 | for unit in dag.sorted(): 40 | # add the unit to networkx representation 41 | G.add_node(unit) 42 | creator_id = dag.pid(unit) 43 | 44 | # set height[unit] 45 | height[unit] = max([height[parent] for parent in dag.parents(unit)], default=-1) + 1 46 | 47 | for parent in dag.parents(unit): 48 | # add an edge to the networkx representation 49 | G.add_edge(unit, parent) 50 | 51 | # set self_predecessor[unit] 52 | self_predecessor[unit] = dag.self_predecessor(creator_id, dag.parents(unit)) 53 | # set self_descendant 54 | if self_predecessor[unit]: 55 | predecessor = self_predecessor[unit] 56 | if predecessor in self_descendant: 57 | self_descendant[predecessor].append(unit) 58 | else: 59 | self_descendant[predecessor] = [unit] 60 | 61 | # set branch[creator_id][unit] 62 | if self_predecessor[unit] is None: 63 | branch[creator_id][unit] = 0 64 | elif len(self_descendant[self_predecessor[unit]]) == 1: 65 | branch[creator_id][unit] = branch[creator_id][self_predecessor[unit]] 66 | else: 67 | branch[creator_id][unit] = max(branch[creator_id].values())+1 68 | 69 | pos = {} 70 | 71 | # find positions of units in the plot 72 | # we plot units created by a given process vertically 73 | # we use height[unit] for its height in the plot 74 | x = dict(zip(range(dag.n_processes), np.linspace(27, 243, dag.n_processes))) 75 | dx = x[1]-x[0] 76 | for pid in range(dag.n_processes): 77 | units_per_pid = [unit for unit in dag if pid == dag.pid(unit)] 78 | x_per_pid = [] 79 | heights = [height[unit] for unit in units_per_pid] 80 | err = 0 81 | spaces = 60 * np.array(heights) + err + 70 82 | y = dict(zip(units_per_pid, spaces)) 83 | 84 | n_branches = len(set(branch[pid].values())) 85 | branch_x = np.linspace(-dx/2+5, dx/2-5, n_branches) 86 | for unit in units_per_pid: 87 | pos_y = y[unit] 88 | pos_x = x[dag.pid(unit)] 89 | if n_branches > 1: 90 | pos_x += branch_x[branch[pid][unit]] 91 | 92 | x_per_pid.append(pos_x) 93 | pos[unit] = (pos_x, pos_y) 94 | 95 | color_values = np.linspace(0, 1, dag.n_processes+1)[1:] 96 | color_map = dict(zip(range(dag.n_processes), color_values)) 97 | color_map[-1] = 0 98 | node_color = [color_map[dag.pid(unit)] for unit in G.nodes()] 99 | nx.draw(G, pos, with_labels=True, arrows=True, node_color=node_color, node_size=1000, cmap=plt.get_cmap('jet')) 100 | plt.show() 101 | 102 | 103 | def plot_poset(poset): 104 | ''' 105 | Given a poset, generates and shows its plot. 106 | ''' 107 | dag, _ = dag_from_poset(poset) 108 | plot_dag(dag) 109 | -------------------------------------------------------------------------------- /aleph/utils/timer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import gc 15 | from logging import Logger 16 | from time import perf_counter as get_time 17 | 18 | 19 | class timer: 20 | """ 21 | A simple timer for small snippets of code, usable as a context manager. 22 | 23 | Usage: 24 | 25 | with timer('group', 'somename'): 26 | code1 27 | code2 28 | code3 29 | 30 | with timer('group', 'somename', disable_gc=True): #disables garbage collector in the whole block (this is the default setting) 31 | code1 32 | ... 33 | 34 | timer.write_summary(where, groups) 35 | #*where* can be a Logger instance, None (stdout - default) or any object with callable write() attribute 36 | #*groups* - print summary only for chosen groups. By default prints all groups 37 | 38 | timer.reset(group) 39 | #forgets about everything that was recorded with timers from a given group. If *group* is None, forgets everything 40 | """ 41 | 42 | results = {} 43 | 44 | def __init__(self, group, name, disable_gc=True): 45 | self.group = group 46 | self.name = name 47 | self.disable_gc = disable_gc 48 | 49 | 50 | def __enter__(self): 51 | if self.disable_gc: 52 | self.old_gc = gc.isenabled() 53 | gc.disable() 54 | self.start = get_time() 55 | return self 56 | 57 | 58 | def __exit__(self, exc_type, exc_val, exc_tb): 59 | end = get_time() 60 | if self.disable_gc and self.old_gc: 61 | gc.enable() 62 | if self.group not in self.results: 63 | self.results[self.group] = {} 64 | g = self.results[self.group] 65 | if self.name not in g: 66 | g[self.name] = 0.0 67 | g[self.name] += end - self.start 68 | 69 | 70 | @classmethod 71 | def write_summary(cls, where=None, groups=None): 72 | if where is None: 73 | write = print 74 | elif isinstance(where, Logger): 75 | write = where.info 76 | elif hasattr(where, 'write') and callable(where.write): 77 | write = where.write 78 | 79 | groups = groups or list(sorted(cls.results.keys())) 80 | 81 | for group in groups: 82 | if group in cls.results: 83 | for name, time in cls.results[group].items(): 84 | write(f'timer {str(group)} | {name} took {time:.6f} s') 85 | 86 | 87 | @classmethod 88 | def reset(cls, group=None): 89 | if group is None: 90 | cls.results = {} 91 | elif group in cls.results: 92 | del cls.results[group] 93 | 94 | -------------------------------------------------------------------------------- /benchmarks/signing.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import coincurve 15 | import nacl.signing 16 | from time import time 17 | 18 | bench_size = 100000 19 | 20 | print('bench size', bench_size) 21 | 22 | messages = [str(i).encode() for i in range(bench_size)] 23 | 24 | sk_cc = coincurve.PrivateKey() 25 | 26 | print('benchmarking signing') 27 | 28 | time_cc = time() 29 | signs_cc = [] 30 | for msg in messages: 31 | signs_cc.append(sk_cc.sign(msg)) 32 | 33 | time_cc = round(time() - time_cc, 2) 34 | 35 | sk_nc = nacl.signing.SigningKey.generate() 36 | 37 | time_nc = time() 38 | signs_nc = [] 39 | for msg in messages: 40 | signs_nc.append(sk_nc.sign(msg)) 41 | 42 | time_nc = round(time() - time_nc, 2) 43 | 44 | print(f'coincurve {time_cc} nacl {time_nc}') 45 | 46 | 47 | print('benchmarking verification') 48 | 49 | vk_cc = coincurve.PublicKey.from_secret(sk_cc.secret) 50 | signs_msg_cc = zip(signs_cc, messages) 51 | time_cc = time() 52 | for sig, msg in signs_msg_cc: 53 | vk_cc.verify(sig, msg) 54 | 55 | time_cc = round(time() - time_cc, 2) 56 | 57 | vk_nc = sk_nc.verify_key 58 | signs_msg_nc = zip(signs_nc, messages) 59 | time_nc = time() 60 | for sig, msg in signs_msg_nc: 61 | vk_nc.verify(sig) == msg 62 | 63 | time_nc = round(time() - time_nc, 2) 64 | 65 | print(f'coincurve {time_cc} nacl {time_nc}') 66 | -------------------------------------------------------------------------------- /benchmarks/threshold_coin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from time import time 15 | from tqdm import tqdm 16 | from random import randint, sample 17 | 18 | from aleph.crypto import ThresholdCoin, generate_keys 19 | 20 | n_parties, threshold = 1000, 667 21 | VK, SKs = generate_keys(n_parties, threshold) 22 | 23 | dealer_id = randint(0, n_parties) 24 | TCs = [ThresholdCoin(dealer_id, pid, n_parties, threshold, SK, VK) for pid, SK in enumerate(SKs)] 25 | 26 | n_examples = 1000 27 | 28 | results, times_gen, times_combine = [], [], [] 29 | 30 | for _ in tqdm(range(n_examples)): 31 | nonce = randint(0, 100000) 32 | 33 | # generate coin shares of all parties 34 | start = time() 35 | shares = [TC.create_coin_share(nonce) for TC in TCs] 36 | times_gen.append(time()-start) 37 | 38 | _shares = {i:shares[i] for i in sample(range(n_parties), threshold)} 39 | 40 | start = time() 41 | results.append(TCs[0].combine_coin_shares(_shares)) 42 | times_combine.append(time()-start) 43 | print('time needed for generating one share:', round(sum(times_gen)/len(times_gen)/1000, 4)) 44 | print('time needed for combining shares', round(sum(times_combine)/len(times_combine), 4)) 45 | print('mean value: ', sum(results)/len(results)) 46 | -------------------------------------------------------------------------------- /benchmarks/toss_coin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import random 15 | from time import time 16 | from joblib import Parallel, delayed 17 | 18 | from aleph.utils.generic_test import simulate_with_checks 19 | 20 | def time_coin_toss(U, poset, dag, results, additional_args): 21 | if additional_args is None: 22 | levels, U_c = set(), None 23 | else: 24 | levels, U_c = additional_args 25 | if U.level not in levels: 26 | levels.add(U.level) 27 | if U.level == 3: 28 | U_c = U 29 | if U_c is not None and U.level-U_c.level >= 4: 30 | start = time() 31 | poset.toss_coin(U_c, U) 32 | end = time()-start 33 | results.append(end) 34 | return levels, U_c 35 | 36 | def measure_time(n_jobs, n_processes, n_units, n_forkers): 37 | print('n_jobs', n_jobs, '\nn_processes', n_processes, '\nn_units', n_units, '\nn_forkers', n_forkers) 38 | print('dispatching workers') 39 | start = time() 40 | results = Parallel(n_jobs=n_jobs)( 41 | delayed(simulate_with_checks)( 42 | n_processes, 43 | n_units, 44 | n_forkers, 45 | post_prepare = time_coin_toss, 46 | seed = round(time())+i 47 | ) for i in range(n_jobs)) 48 | delta = time()-start 49 | if delta < 60: 50 | print('work done in', round(delta, 2)) 51 | else: 52 | print('work done in', round(delta/60, 2)) 53 | all_sum, all_len = 0, 0 54 | for res in results: 55 | all_sum += sum(res) 56 | all_len += len(res) 57 | print(all_len, all_sum/all_len, [len(res) for res in results]) 58 | 59 | if __name__ == '__main__': 60 | measure_time(8, 4, 1000, 0) 61 | measure_time(8, 100, 50000, 0) 62 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/build/html/_static/classic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * classic.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- classic theme. 6 | * 7 | * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: sans-serif; 18 | font-size: 100%; 19 | background-color: #11303d; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: #1c4e63; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 230px; 36 | } 37 | 38 | div.body { 39 | background-color: #ffffff; 40 | color: #000000; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | div.footer { 45 | color: #ffffff; 46 | width: 100%; 47 | padding: 9px 0 9px 0; 48 | text-align: center; 49 | font-size: 75%; 50 | } 51 | 52 | div.footer a { 53 | color: #ffffff; 54 | text-decoration: underline; 55 | } 56 | 57 | div.related { 58 | background-color: #133f52; 59 | line-height: 30px; 60 | color: #ffffff; 61 | } 62 | 63 | div.related a { 64 | color: #ffffff; 65 | } 66 | 67 | div.sphinxsidebar { 68 | } 69 | 70 | div.sphinxsidebar h3 { 71 | font-family: 'Trebuchet MS', sans-serif; 72 | color: #ffffff; 73 | font-size: 1.4em; 74 | font-weight: normal; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | 79 | div.sphinxsidebar h3 a { 80 | color: #ffffff; 81 | } 82 | 83 | div.sphinxsidebar h4 { 84 | font-family: 'Trebuchet MS', sans-serif; 85 | color: #ffffff; 86 | font-size: 1.3em; 87 | font-weight: normal; 88 | margin: 5px 0 0 0; 89 | padding: 0; 90 | } 91 | 92 | div.sphinxsidebar p { 93 | color: #ffffff; 94 | } 95 | 96 | div.sphinxsidebar p.topless { 97 | margin: 5px 10px 10px 10px; 98 | } 99 | 100 | div.sphinxsidebar ul { 101 | margin: 10px; 102 | padding: 0; 103 | color: #ffffff; 104 | } 105 | 106 | div.sphinxsidebar a { 107 | color: #98dbcc; 108 | } 109 | 110 | div.sphinxsidebar input { 111 | border: 1px solid #98dbcc; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | 116 | 117 | 118 | /* -- hyperlink styles ------------------------------------------------------ */ 119 | 120 | a { 121 | color: #355f7c; 122 | text-decoration: none; 123 | } 124 | 125 | a:visited { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:hover { 131 | text-decoration: underline; 132 | } 133 | 134 | 135 | 136 | /* -- body styles ----------------------------------------------------------- */ 137 | 138 | div.body h1, 139 | div.body h2, 140 | div.body h3, 141 | div.body h4, 142 | div.body h5, 143 | div.body h6 { 144 | font-family: 'Trebuchet MS', sans-serif; 145 | background-color: #f2f2f2; 146 | font-weight: normal; 147 | color: #20435c; 148 | border-bottom: 1px solid #ccc; 149 | margin: 20px -20px 10px -20px; 150 | padding: 3px 0 3px 10px; 151 | } 152 | 153 | div.body h1 { margin-top: 0; font-size: 200%; } 154 | div.body h2 { font-size: 160%; } 155 | div.body h3 { font-size: 140%; } 156 | div.body h4 { font-size: 120%; } 157 | div.body h5 { font-size: 110%; } 158 | div.body h6 { font-size: 100%; } 159 | 160 | a.headerlink { 161 | color: #c60f0f; 162 | font-size: 0.8em; 163 | padding: 0 4px 0 4px; 164 | text-decoration: none; 165 | } 166 | 167 | a.headerlink:hover { 168 | background-color: #c60f0f; 169 | color: white; 170 | } 171 | 172 | div.body p, div.body dd, div.body li, div.body blockquote { 173 | text-align: justify; 174 | line-height: 130%; 175 | } 176 | 177 | div.admonition p.admonition-title + p { 178 | display: inline; 179 | } 180 | 181 | div.admonition p { 182 | margin-bottom: 5px; 183 | } 184 | 185 | div.admonition pre { 186 | margin-bottom: 5px; 187 | } 188 | 189 | div.admonition ul, div.admonition ol { 190 | margin-bottom: 5px; 191 | } 192 | 193 | div.note { 194 | background-color: #eee; 195 | border: 1px solid #ccc; 196 | } 197 | 198 | div.seealso { 199 | background-color: #ffc; 200 | border: 1px solid #ff6; 201 | } 202 | 203 | div.topic { 204 | background-color: #eee; 205 | } 206 | 207 | div.warning { 208 | background-color: #ffe4e4; 209 | border: 1px solid #f66; 210 | } 211 | 212 | p.admonition-title { 213 | display: inline; 214 | } 215 | 216 | p.admonition-title:after { 217 | content: ":"; 218 | } 219 | 220 | pre { 221 | padding: 5px; 222 | background-color: #eeffcc; 223 | color: #333333; 224 | line-height: 120%; 225 | border: 1px solid #ac9; 226 | border-left: none; 227 | border-right: none; 228 | } 229 | 230 | code { 231 | background-color: #ecf0f3; 232 | padding: 0 1px 0 1px; 233 | font-size: 0.95em; 234 | } 235 | 236 | th { 237 | background-color: #ede; 238 | } 239 | 240 | .warning code { 241 | background: #efc2c2; 242 | } 243 | 244 | .note code { 245 | background: #d6d6d6; 246 | } 247 | 248 | .viewcode-back { 249 | font-family: sans-serif; 250 | } 251 | 252 | div.viewcode-block:target { 253 | background-color: #f4debf; 254 | border-top: 1px solid #ac9; 255 | border-bottom: 1px solid #ac9; 256 | } 257 | 258 | div.code-block-caption { 259 | color: #efefef; 260 | background-color: #1c4e63; 261 | } -------------------------------------------------------------------------------- /docs/build/html/_static/default.css: -------------------------------------------------------------------------------- 1 | @import url("classic.css"); 2 | -------------------------------------------------------------------------------- /docs/build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.1', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | FILE_SUFFIX: '.html', 7 | HAS_SOURCE: true, 8 | SOURCELINK_SUFFIX: '.txt', 9 | NAVIGATION_WITH_KEYS: false 10 | }; -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | // global elements used by the functions. 34 | // the 'sidebarbutton' element is defined as global after its 35 | // creation, in the add_sidebar_button function 36 | var bodywrapper = $('.bodywrapper'); 37 | var sidebar = $('.sphinxsidebar'); 38 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 39 | 40 | // for some reason, the document has no sidebar; do not run into errors 41 | if (!sidebar.length) return; 42 | 43 | // original margin-left of the bodywrapper and width of the sidebar 44 | // with the sidebar expanded 45 | var bw_margin_expanded = bodywrapper.css('margin-left'); 46 | var ssb_width_expanded = sidebar.width(); 47 | 48 | // margin-left of the bodywrapper and width of the sidebar 49 | // with the sidebar collapsed 50 | var bw_margin_collapsed = '.8em'; 51 | var ssb_width_collapsed = '.8em'; 52 | 53 | // colors used by the current theme 54 | var dark_color = $('.related').css('background-color'); 55 | var light_color = $('.document').css('background-color'); 56 | 57 | function sidebar_is_collapsed() { 58 | return sidebarwrapper.is(':not(:visible)'); 59 | } 60 | 61 | function toggle_sidebar() { 62 | if (sidebar_is_collapsed()) 63 | expand_sidebar(); 64 | else 65 | collapse_sidebar(); 66 | } 67 | 68 | function collapse_sidebar() { 69 | sidebarwrapper.hide(); 70 | sidebar.css('width', ssb_width_collapsed); 71 | bodywrapper.css('margin-left', bw_margin_collapsed); 72 | sidebarbutton.css({ 73 | 'margin-left': '0', 74 | 'height': bodywrapper.height() 75 | }); 76 | sidebarbutton.find('span').text('»'); 77 | sidebarbutton.attr('title', _('Expand sidebar')); 78 | document.cookie = 'sidebar=collapsed'; 79 | } 80 | 81 | function expand_sidebar() { 82 | bodywrapper.css('margin-left', bw_margin_expanded); 83 | sidebar.css('width', ssb_width_expanded); 84 | sidebarwrapper.show(); 85 | sidebarbutton.css({ 86 | 'margin-left': ssb_width_expanded-12, 87 | 'height': bodywrapper.height() 88 | }); 89 | sidebarbutton.find('span').text('«'); 90 | sidebarbutton.attr('title', _('Collapse sidebar')); 91 | document.cookie = 'sidebar=expanded'; 92 | } 93 | 94 | function add_sidebar_button() { 95 | sidebarwrapper.css({ 96 | 'float': 'left', 97 | 'margin-right': '0', 98 | 'width': ssb_width_expanded - 28 99 | }); 100 | // create the button 101 | sidebar.append( 102 | '
«
' 103 | ); 104 | var sidebarbutton = $('#sidebarbutton'); 105 | light_color = sidebarbutton.css('background-color'); 106 | // find the height of the viewport to center the '<<' in the page 107 | var viewport_height; 108 | if (window.innerHeight) 109 | viewport_height = window.innerHeight; 110 | else 111 | viewport_height = $(window).height(); 112 | sidebarbutton.find('span').css({ 113 | 'display': 'block', 114 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 115 | }); 116 | 117 | sidebarbutton.click(toggle_sidebar); 118 | sidebarbutton.attr('title', _('Collapse sidebar')); 119 | sidebarbutton.css({ 120 | 'color': '#FFFFFF', 121 | 'border-left': '1px solid ' + dark_color, 122 | 'font-size': '1.2em', 123 | 'cursor': 'pointer', 124 | 'height': bodywrapper.height(), 125 | 'padding-top': '1px', 126 | 'margin-left': ssb_width_expanded - 12 127 | }); 128 | 129 | sidebarbutton.hover( 130 | function () { 131 | $(this).css('background-color', dark_color); 132 | }, 133 | function () { 134 | $(this).css('background-color', light_color); 135 | } 136 | ); 137 | } 138 | 139 | function set_position_from_cookie() { 140 | if (!document.cookie) 141 | return; 142 | var items = document.cookie.split(';'); 143 | for(var k=0; k 3 | 4 | 5 | 6 | 7 | 5. Actions — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

5. Actions

48 |
49 | 53 |
54 |
55 | 56 | 57 |
58 |
59 |
60 | 87 |
88 |
89 | 107 | 111 | 112 | -------------------------------------------------------------------------------- /docs/build/html/crypto/crypto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 6. Crypto — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

6. Crypto

48 | 56 |
57 | 58 | 59 |
60 |
61 |
62 | 89 |
90 |
91 | 109 | 113 | 114 | -------------------------------------------------------------------------------- /docs/build/html/data_structures/data_structures.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 3. Data structures — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

3. Data structures

48 |
49 | 54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 | 88 |
89 |
90 | 108 | 112 | 113 | -------------------------------------------------------------------------------- /docs/build/html/data_structures/tx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 3.3. Tx — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |

3.3. Tx

49 |
50 |
51 | class aleph.data_structures.tx.Tx(issuer, receiver, amount)
52 |

This class stores a transactions issued by some user

53 |
54 |
Parameters
55 |
    56 |
  • issuer (str) – public key of the issuer of the transaction

  • 57 |
  • receiver (str) – public key of the receiver of the transaction

  • 58 |
  • amount (int) – amount to be sent to the receiver

  • 59 |
60 |
61 |
62 |
63 | 64 |
65 | 66 | 67 |
68 |
69 |
70 | 97 |
98 |
99 | 118 | 122 | 123 | -------------------------------------------------------------------------------- /docs/build/html/intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 1. Introduction — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

1. Introduction

48 |

Welcome to the documentation of AlephZero Proof-of-Concept implementation in Python.

49 |

The AlephZero protocol is a consensus protocol that is well-suited for usage in a blockchain project. 50 | For the detailed description, check the first version of the paper and the second version of the `paper <>`_.

51 |

This documentation describes the most important classes and methods of the implementation and assumes the thorought understanding of the papers.

52 |
53 | 54 | 55 |
56 |
57 |
58 | 85 |
86 |
87 | 105 | 109 | 110 | -------------------------------------------------------------------------------- /docs/build/html/network/net.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 4. Network — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |

4. Network

48 |
49 | 53 |
54 |
55 | 56 | 57 |
58 |
59 |
60 | 87 |
88 |
89 | 107 | 111 | 112 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aleph-zero-foundation/Proof-of-Concept/4eb50a9d946a8f2ac2483e3acd5af1e88b4214d8/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Search — AlephZero 0.1 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 37 | 38 |
39 |
40 |
41 |
42 | 43 |

Search

44 |
45 | 46 |

47 | Please activate JavaScript to enable the search 48 | functionality. 49 |

50 |
51 |

52 | From here you can search these documents. Enter your search 53 | words into the box below and click "search". Note that the search 54 | function will automatically search for all of the words. Pages 55 | containing fewer words won't appear in the result list. 56 |

57 |
58 | 59 | 60 | 61 |
62 | 63 |
64 | 65 |
66 | 67 |
68 |
69 |
70 | 74 |
75 |
76 | 88 | 92 | 93 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/actions/actions.rst: -------------------------------------------------------------------------------- 1 | Actions 2 | ------------------------- 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | create_unit 8 | poset_syncing 9 | -------------------------------------------------------------------------------- /docs/source/actions/create_unit.rst: -------------------------------------------------------------------------------- 1 | Create Unit 2 | ------------------------- 3 | 4 | .. automodule:: aleph.actions.create_unit 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/actions/poset_syncing.rst: -------------------------------------------------------------------------------- 1 | Poset Syncing 2 | ------------------------- 3 | 4 | .. automodule:: aleph.actions.poset_syncing 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/aleph_1920x1080.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aleph-zero-foundation/Proof-of-Concept/4eb50a9d946a8f2ac2483e3acd5af1e88b4214d8/docs/source/aleph_1920x1080.jpg -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | # Configuration file for the Sphinx documentation builder. 15 | # 16 | # This file only contains a selection of the most common options. For a full 17 | # list see the documentation: 18 | # http://www.sphinx-doc.org/en/master/config 19 | 20 | # -- Path setup -------------------------------------------------------------- 21 | 22 | # If extensions (or modules to document with autodoc) are in another directory, 23 | # add these directories to sys.path here. If the directory is relative to the 24 | # documentation root, use os.path.abspath to make it absolute, like shown here. 25 | # 26 | import os 27 | import sys 28 | sys.path.insert(0, os.path.abspath('../../aleph')) 29 | 30 | 31 | # -- Project information ----------------------------------------------------- 32 | 33 | project = 'AlephZero' 34 | copyright = '2019, Michal Swietek' 35 | author = 'Michal Swietek' 36 | 37 | # The full version, including alpha/beta/rc tags 38 | release = '0.1' 39 | 40 | 41 | # -- General configuration --------------------------------------------------- 42 | 43 | # Add any Sphinx extension module names here, as strings. They can be 44 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 45 | # ones. 46 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage'] 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # List of patterns, relative to source directory, that match files and 52 | # directories to ignore when looking for source files. 53 | # This pattern also affects html_static_path and html_extra_path. 54 | exclude_patterns = [] 55 | 56 | 57 | # -- Options for HTML output ------------------------------------------------- 58 | 59 | # The theme to use for HTML and HTML Help pages. See the documentation for 60 | # a list of builtin themes. 61 | # 62 | html_theme = 'default' 63 | 64 | # Add any paths that contain custom static files (such as style sheets) here, 65 | # relative to this directory. They are copied after the builtin static files, 66 | # so a file named "default.css" will overwrite the builtin "default.css". 67 | html_static_path = ['_static'] 68 | -------------------------------------------------------------------------------- /docs/source/crypto/crp.rst: -------------------------------------------------------------------------------- 1 | Common Random Permutation 2 | ------------------------- 3 | 4 | .. automodule:: aleph.crypto.crp 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/crypto/crypto.rst: -------------------------------------------------------------------------------- 1 | Crypto 2 | ------------------------- 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | crp 8 | keys 9 | threshold_signatures 10 | threshold_coin 11 | -------------------------------------------------------------------------------- /docs/source/crypto/keys.rst: -------------------------------------------------------------------------------- 1 | Signing Keys 2 | ------------------------- 3 | 4 | .. automodule:: aleph.crypto.keys 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/crypto/threshold_coin.rst: -------------------------------------------------------------------------------- 1 | Threshold Coin 2 | ------------------------- 3 | 4 | .. automodule:: aleph.crypto.threshold_coin 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/crypto/threshold_signatures.rst: -------------------------------------------------------------------------------- 1 | Threshold Signatures 2 | ------------------------- 3 | 4 | .. automodule:: aleph.crypto.threshold_signatures 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/data_structures/data_structures.rst: -------------------------------------------------------------------------------- 1 | Data structures 2 | ------------------------- 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | unit 8 | poset 9 | tx 10 | -------------------------------------------------------------------------------- /docs/source/data_structures/poset.rst: -------------------------------------------------------------------------------- 1 | Poset 2 | ------------------------- 3 | 4 | .. automodule:: aleph.data_structures.poset 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/data_structures/tx.rst: -------------------------------------------------------------------------------- 1 | Tx 2 | ------------------------- 3 | 4 | .. automodule:: aleph.data_structures.tx 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/data_structures/unit.rst: -------------------------------------------------------------------------------- 1 | Unit 2 | ------------------------- 3 | 4 | .. automodule:: aleph.data_structures.unit 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to AlephZero's documentation! 2 | ===================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | :caption: Contents: 7 | :numbered: 8 | 9 | intro 10 | process 11 | data_structures/data_structures 12 | network/net 13 | actions/actions 14 | crypto/crypto 15 | 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | -------------------------------------------------------------------------------- /docs/source/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ========================= 3 | 4 | Welcome to the documentation of Aleph Proof-of-Concept implementation in Python. 5 | 6 | The Aleph protocol is a consensus protocol that is well-suited for usage in a blockchain project. 7 | For the detailed description, check the first version of the `paper `_ and the second version of the `paper `_. 8 | 9 | This documentation describes the most important classes and methods of the implementation and assumes the thorought understanding of the papers. 10 | -------------------------------------------------------------------------------- /docs/source/network/channel.rst: -------------------------------------------------------------------------------- 1 | Channel 2 | ------------------------- 3 | 4 | .. automodule:: aleph.network.channel 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/network/net.rst: -------------------------------------------------------------------------------- 1 | Network 2 | ------------------------- 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | channel 8 | network 9 | -------------------------------------------------------------------------------- /docs/source/network/network.rst: -------------------------------------------------------------------------------- 1 | Network 2 | ------------------------- 3 | 4 | .. automodule:: aleph.network.network 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/process.rst: -------------------------------------------------------------------------------- 1 | Process 2 | ------------------------- 3 | 4 | .. automodule:: aleph.process 5 | :members: 6 | -------------------------------------------------------------------------------- /examples/plot.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import sys 15 | 16 | from aleph.utils.dag_utils import dag_from_file, generate_random_forking, generate_random_nonforking 17 | from aleph.utils.plot import plot_dag 18 | 19 | path = "" 20 | if len(sys.argv) == 2: 21 | path = sys.argv[1] 22 | elif len(sys.argv) == 3: 23 | n_processes, n_units = int(sys.argv[1]), int(sys.argv[2]) 24 | path = 'random_nonforking_{}_{}.txt'.format(n_processes, n_units) 25 | generate_random_nonforking(n_processes, n_units, path) 26 | elif len(sys.argv) == 4: 27 | n_processes, n_units, n_forkers = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]) 28 | path = 'random_forking_{}_{}_{}.txt'.format(n_processes, n_units, n_forkers) 29 | generate_random_forking(n_processes, n_units, n_forkers, path) 30 | else: 31 | print("Either provide a file to plot or number of processes, units and (optionally) forkers.") 32 | sys.exit(1) 33 | dag = dag_from_file(path) 34 | plot_dag(dag) 35 | -------------------------------------------------------------------------------- /examples/random_forking_10_30_5.txt: -------------------------------------------------------------------------------- 1 | format standard 2 | 10 3 | 0,8 8 4 | 0,2 2 5 | 0,7 7 6 | 0,3 3 7 | 1,7 7 0,7 0,3 8 | 2,7 7 0,2 1,7 9 | 1,8 8 0,8 2,7 10 | 0,1 1 11 | 2,8 8 1,8 0,1 12 | 0,0 0 13 | 1,3 3 1,7 0,0 14 | 0,6 6 15 | 2,3 3 1,3 0,6 16 | 3,7 7 0,1 1,8 17 | 1,0 0 2,3 3,7 18 | 0,9 9 19 | 1,3,1 3 0,9 1,8 20 | 2,0 0 1,0 1,3,1 21 | 1,1,1 1 1,0 0,3 22 | 1,0,1 0 2,3 0,2 23 | 0,4 4 24 | 1,2 2 0,2 0,3 25 | 1,3,2 3 0,2 1,2 26 | 2,2 2 1,3,2 3,7 27 | 1,1,2 1 0,4 2,2 28 | 1,1 1 1,0 1,2 29 | 1,6 6 1,1 3,7 30 | 1,3,3 3 0,0 1,7 31 | 2,0,1 0 1,6 1,3,3 32 | 1,4 4 1,2 0,4 33 | 0,5 5 34 | 2,3,1 3 1,3,2 0,1 35 | 3,3 3 0,5 2,3,1 36 | 4,3 3 1,4 3,3 37 | 1,9 9 1,0 0,9 38 | 3,3,1 3 1,9 0,1 39 | 4,7 7 1,9 2,3,1 40 | 1,1,3 1 4,7 3,3 41 | 2,0,2 0 1,9 1,1,3 42 | 2,0,3 0 1,6 2,3,1 43 | -------------------------------------------------------------------------------- /examples/random_nonforking_10_30.txt: -------------------------------------------------------------------------------- 1 | format standard 2 | 10 3 | 0,7 7 4 | 0,0 0 5 | 1,7 7 0,7 0,0 6 | 0,9 9 7 | 2,7 7 1,7 0,9 8 | 0,1 1 9 | 0,2 2 10 | 0,8 8 11 | 1,2 2 0,2 0,8 12 | 1,1 1 0,1 1,2 13 | 0,4 4 14 | 1,4 4 0,4 0,2 15 | 1,8 8 0,8 1,4 16 | 0,5 5 17 | 2,8 8 1,8 0,5 18 | 2,1 1 1,1 2,8 19 | 3,1 1 2,1 2,8 20 | 1,5 5 0,5 0,0 21 | 0,3 3 22 | 0,6 6 23 | 1,3 3 0,3 0,6 24 | 2,5 5 1,5 1,3 25 | 4,1 1 3,1 2,5 26 | 3,5 5 2,5 1,2 27 | 4,5 5 3,5 2,8 28 | 2,4 4 1,4 0,0 29 | 3,4 4 2,4 0,9 30 | 1,6 6 0,6 0,0 31 | 1,0 0 0,0 1,6 32 | 4,4 4 3,4 1,0 33 | 1,9 9 0,9 1,6 34 | 2,2 2 1,2 1,9 35 | 2,0 0 1,0 2,2 36 | 2,6 6 1,6 2,8 37 | 3,2 2 2,2 2,6 38 | 3,8 8 2,8 2,6 39 | 2,3 3 1,3 2,2 40 | 3,3 3 2,3 2,6 41 | 4,8 8 3,8 3,3 42 | 2,9 9 1,9 4,8 43 | -------------------------------------------------------------------------------- /experiments/aws/aws_deps.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | print () { 6 | echo -e "\033[0;32m$@\033[0m" 7 | } 8 | 9 | print installing parallel 10 | sudo apt-get install -y parallel 11 | 12 | print enterintg virtenv p37 13 | 14 | source $HOME/p37/bin/activate 15 | 16 | print installing fabric, boto3, ipython 17 | pip install fabric boto3 ipython 18 | -------------------------------------------------------------------------------- /experiments/aws/const.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | N_PARENTS = 10 # maximal number of parents a unit can have 15 | 16 | CREATE_DELAY = 2.0 # delay after creating a new unit 17 | STEP_SIZE = 0.14 # a number in (0,1) describing how aggresive is the create_delay adjusting mechanism, large = aggresive 18 | 19 | SYNC_INIT_DELAY = 0.015625 # delay after initianing a sync with other processes 20 | 21 | N_RECV_SYNC = 10 # number of allowed parallel received syncs 22 | N_INIT_SYNC = 10 # number of allowed parallel initiated syncs 23 | 24 | TXPU = 1 # number of transactions per unit 25 | TX_LIMIT = 1000000 # limit of all txs generated for one process 26 | 27 | LEVEL_LIMIT = 20 # maximal level after which process shuts down 28 | UNITS_LIMIT = None # maximal number of units that are constructed 29 | SYNCS_LIMIT = None # maximal number of syncs that are performed 30 | 31 | USE_TCOIN = 1 # whether to use threshold coin 32 | PRECOMPUTE_POPULARITY = 0 # precompute popularity proof to ease computational load of Poset.compute_vote procedure 33 | ADAPTIVE_DELAY = 0 # whether to use the adaptive strategy of determining create_delay 34 | 35 | VOTING_LEVEL = 3 # level at which the first voting round occurs, this is "t" from the write-up 36 | PI_DELTA_LEVEL = 12 # level at which to switch from the "fast" to the pi_delta algorithm 37 | ADD_SHARES = PI_DELTA_LEVEL - 1 # level at which to start adding coin shares to units, it's safe to make it PI_DELTA_LEVEL - 1 38 | 39 | HOST_IP = '127.0.0.1' # default ip address of a process 40 | HOST_PORT = 8888 # default port of incoming syncs 41 | 42 | LOGGER_NAME = 'aleph' # name of our logger and logfile 43 | TX_SOURCE = 'tx_source_gen' # source of txs 44 | -------------------------------------------------------------------------------- /experiments/aws/key_pairs/aleph.fingerprint: -------------------------------------------------------------------------------- 1 | eb:8f:e8:c5:cf:d6:23:f5:da:e4:81:b1:76:2d:d3:bc -------------------------------------------------------------------------------- /experiments/aws/key_pairs/aleph.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAqVPgYvWw4Q9XeuPWYxMurO90cWuruaWPTRGA0UHc/qUBVfNN 3 | WZdPJTtlCG8vd3p5nmfBC0WSa0YLQjFcsYlQ7+ueec9v7ZDErxCCG8cuqAV/yieZ 4 | kL/lgZJF9Q8F4x5bRu9XASI5ZAMSyQOAGigOBfPXNagaUgD+lQ/FcyozVC6QXz7K 5 | fYZDfvJAPpB6ywFNbkM8mKtLqMsnV8uV7X/s6ohDsiW+sIVQq/3Xh2CQ/kUJndmy 6 | 7/+DV3vRemiCSnfFPXAsPZv0zsG4bl9pqWOBNgbLyRTEF8hMiihHPh/ZgsFGLeeu 7 | B3F2tqJ1oIHNhRoEK4sMR7W2Xmt7Q408DgkEawIDAQABAoIBABj3XAtbTIQyneRp 8 | uY+MIYKwH8wlYwGRbqqfXQ39lxcYvzSsFgiSrcHAmyn+MtJ8BXictbLyxB77Ceft 9 | nUw0AfIOHKS7ODaf9NAXMAfme3Ocs/XbluHn2XAcYCI839JYBH9WBM8O+8VKSrfG 10 | w1mX9FFeXan0qkTTDrZtx+AuJblQktUKDFddL27rbyORRFNIFoXn/PNa8nxDPJfe 11 | UFR5uMB8wSq2OuYztlsPJkRmcETQ9oZy6R6eKLfIVrbAJ9qKkrgmCYYCqb0QFUx9 12 | in6BQ3ZcbDVXBrsty7MmYjxbJc5Tli2wkdL5sxiNFqqZ6wBU0RHViq3QVMVKCo6n 13 | rAkIwVECgYEA2eVG91AAGbXnJEjpYSaVEbdB2pB33ffTtC4sCTvanMft1jBg66sE 14 | EI2PATZp70p1V7SqnRqVbST7WK75KI8PammQBbDh0CUZMLv3k2F/p4fGb5kVyYby 15 | nkrp6yH5zj7vBDKXAAbaol0JD8bzPnNxKZor5TXutBOHE9hMrqXz8tkCgYEAxvBQ 16 | igv4YQvqLVa89aaIAL31n7mFHCrXQ9ExVZFnE01ZZacWMas6SbcfxiQebr/96nZM 17 | sG6CStBD19k29TMrmZHqOf6CQAK0kyBocmAO0pBlpZr3vo3agWwzHdvXJOW8JjyF 18 | R+Jz9ouqbU1yij2dsI88bvYlJh7yFA3b403VXuMCgYAuja/V0v/Sfmy98d365bVf 19 | rZmiSs9xWyueCWdbKbCRYp3L71Ylw8oAh14QUkHb4Dql3HG/Uxit0wLodanCIp6W 20 | i6s7rAjy4Zo6MU657FDLyFR7L2faQLzBWRwVIpMun3+NlAly9gPsQJhZCJabtyps 21 | SwYp2JYBX1KyyLnJbqdqMQKBgEHhPVqp4iQJNRC0o7jZ4DoWriup3TnZuhXcRfEn 22 | SLIssEkojwH9yTy2JxCPz4Avll+5s2XOAtnRpFXrufZzbqHZn257KRkXjEMYvV37 23 | D42NggvYiOiSw2SOt1LRBatTZP5oll6a0Ho6AENYHXFIPFtG4+V/IAhCwToA6Oua 24 | T+mvAoGBAKlzHYX0S65xzIXmaEGhOnSTwcX0J2O2R8J7yliyaQDf/qTTFv6WYBjg 25 | OJxnRFynUHf04mOWUkHc5rb8dMD2pgrDWoHg1hAPaGi8OAa5T/ctl2SEQ+oG7rBY 26 | LCqwoa7bcxp0rEzaNoe5T6S5PGFKgFCE8ONcKPMaZ9rhAuYe0hsW 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /experiments/aws/key_pairs/aleph.pem.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVPgYvWw4Q9XeuPWYxMu 3 | rO90cWuruaWPTRGA0UHc/qUBVfNNWZdPJTtlCG8vd3p5nmfBC0WSa0YLQjFcsYlQ 4 | 7+ueec9v7ZDErxCCG8cuqAV/yieZkL/lgZJF9Q8F4x5bRu9XASI5ZAMSyQOAGigO 5 | BfPXNagaUgD+lQ/FcyozVC6QXz7KfYZDfvJAPpB6ywFNbkM8mKtLqMsnV8uV7X/s 6 | 6ohDsiW+sIVQq/3Xh2CQ/kUJndmy7/+DV3vRemiCSnfFPXAsPZv0zsG4bl9pqWOB 7 | NgbLyRTEF8hMiihHPh/ZgsFGLeeuB3F2tqJ1oIHNhRoEK4sMR7W2Xmt7Q408DgkE 8 | awIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /experiments/aws/set_env.sh: -------------------------------------------------------------------------------- 1 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 2 | export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/lib 3 | export PYTHONPATH=.:/home/ubuntu/proof-of-concept 4 | alias python=python3.7 5 | 6 | -------------------------------------------------------------------------------- /experiments/aws/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | # install in home dir 6 | cd 7 | 8 | echo update > setup.log 9 | sudo apt update 10 | 11 | echo install from ubuntu repo >> setup.log 12 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential make flex bison zip unzip virtualenv libgmp-dev libmpc-dev libssl1.0-dev 13 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y python3-dev python3.7-dev python3-pip 14 | 15 | echo setting up env >> setup.log 16 | virtualenv --python=python3.7 p37 17 | source p37/bin/activate 18 | 19 | echo install from pip repo >> setup.log 20 | pip install setuptools pytest-xdist pynacl networkx numpy matplotlib joblib 21 | 22 | echo install pbc >> setup.log 23 | wget https://crypto.stanford.edu/pbc/files/pbc-0.5.14.tar.gz 24 | tar -xvf pbc-0.5.14.tar.gz 25 | cd pbc-0.5.14 26 | ./configure 27 | make 28 | sudo make install 29 | cd 30 | 31 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 32 | export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/lib 33 | 34 | echo install charm >> setup.log 35 | git clone https://github.com/JHUISI/charm.git 36 | cd charm 37 | ./configure.sh 38 | make 39 | pip install . 40 | cd 41 | 42 | echo done >> setup.log 43 | -------------------------------------------------------------------------------- /experiments/simple_ec2_test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import asyncio 15 | import multiprocessing 16 | import random 17 | 18 | from optparse import OptionParser 19 | 20 | from aleph.crypto.keys import SigningKey, VerifyKey 21 | from aleph.data_structures import UserDB, Tx 22 | from aleph.process import Process 23 | 24 | 25 | def read_hosts_ip(hosts_path): 26 | with open(hosts_path, 'r') as f: 27 | return [line[:-1] for line in f] 28 | 29 | 30 | def read_signing_keys(signing_keys_path): 31 | with open(signing_keys_path, 'r') as f: 32 | hexes = [line[:-1].encode() for line in f] 33 | return [SigningKey(hexed) for hexed in hexes] 34 | 35 | 36 | def prepare_DB(): 37 | random.seed(1729) 38 | initial_balances_and_indices = [] 39 | with open('light_nodes_public_keys', 'r') as f: 40 | ln_public_keys = [line[:-1] for line in f] 41 | for i in range(len(ln_public_keys)): 42 | initial_balances_and_indices.append((ln_public_keys[i], random.randrange(10000, 100000), -1)) 43 | 44 | return UserDB(initial_balances_and_indices) 45 | 46 | 47 | def sort_and_get_my_pid(public_keys, signing_keys, hosts_ip): 48 | with open('my_ip') as f: 49 | my_ip = f.readline().strip() 50 | ind = hosts_ip.index(my_ip) 51 | my_pk = public_keys[ind] 52 | 53 | pk_hexes = [pk.to_hex() for pk in public_keys] 54 | arg_sort = [i for i, _ in sorted(enumerate(pk_hexes), key = lambda x: x[1])] 55 | public_keys = [public_keys[i] for i in arg_sort] 56 | signing_keys = [signing_keys[i] for i in arg_sort] 57 | hosts_ip = [hosts_ip[i] for i in arg_sort] 58 | 59 | return public_keys.index(my_pk), public_keys, signing_keys, hosts_ip 60 | 61 | 62 | def tx_source_gen(process_id, n_processes, batch_size, txpu): 63 | ''' 64 | Produces a tx generator and ensures that all processes choose batches from the same set. 65 | :param int batch_size: number of txs for a process to input into the system. 66 | :param int n_processes: number of parties. 67 | :param int txpu: number of txs to be included in one unit. 68 | ''' 69 | 70 | def _tx_source(tx_receiver_address, tx_queue): 71 | ''' 72 | Generates transactions in bundles of size txpu till batch_size is reached 73 | :param None tx_receiver_address: needed only for comatibility of args list with network.tx_listener 74 | :param queue tx_queue: queue for newly generated txs 75 | ''' 76 | # ensure that batches are different 77 | random.seed(process_id) 78 | with open('light_nodes_public_keys', 'r') as f: 79 | ln_public_keys = [line[:-1] for line in f] 80 | 81 | proposed = 0 82 | while proposed. 12 | ''' 13 | 14 | from setuptools import setup, find_packages 15 | 16 | setup( 17 | name="aleph", 18 | version="0.0.1", 19 | packages=find_packages(), 20 | install_requires=[ 21 | "charm-crypto==0.50", 22 | "pynacl", 23 | "networkx", 24 | "numpy", 25 | "matplotlib", 26 | "parse", 27 | "psutil", 28 | "joblib", 29 | "pytest-xdist", 30 | "tqdm" 31 | ], 32 | license="", 33 | package_data={"aleph.test.data": ["simple.dag", "light_nodes_public_keys"]}, 34 | ) 35 | -------------------------------------------------------------------------------- /tests/linear_ordering.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import asyncio 15 | import multiprocessing 16 | import random 17 | 18 | from aleph.data_structures import Unit 19 | from aleph.process import Process 20 | from aleph.crypto.keys import SigningKey, VerifyKey 21 | from aleph.actions import create_unit 22 | 23 | 24 | def translate_parents_and_copy(U, hashes_to_units): 25 | ''' 26 | Takes a unit from the poset of a process A and the mapping hashes->Units for the poset for process B. 27 | Returns a new unit (with correct references to corresponding parent units in B) to be added to B's poset. 28 | The new unit has all the data in the floor/level/... fields erased. 29 | ''' 30 | parent_hashes = [V.hash() for V in U.parents] 31 | parents = [hashes_to_units[V] for V in parent_hashes] 32 | U_new = Unit(U.creator_id, parents, U.transactions(), U.signature, U.coin_shares) 33 | return U_new 34 | 35 | 36 | n_processes = 16 37 | n_units = 1000 38 | use_tcoin = True 39 | processes = [] 40 | host_ports = [8900+i for i in range(n_processes)] 41 | addresses = [('127.0.0.1', port) for port in host_ports] 42 | recv_addresses = [('127.0.0.1', 9100+i) for i in range(n_processes)] 43 | 44 | signing_keys = [SigningKey() for _ in range(n_processes)] 45 | public_keys = [VerifyKey.from_SigningKey(sk) for sk in signing_keys] 46 | 47 | for process_id in range(n_processes): 48 | sk = signing_keys[process_id] 49 | pk = public_keys[process_id] 50 | new_process = Process(n_processes, process_id, sk, pk, addresses, public_keys, recv_addresses[process_id], None, use_tcoin) 51 | processes.append(new_process) 52 | 53 | 54 | for unit_no in range(n_units): 55 | while True: 56 | creator_id = random.choice(range(n_processes)) 57 | process = processes[creator_id] 58 | new_unit = create_unit(process.poset, creator_id, []) 59 | if new_unit is None: 60 | continue 61 | 62 | process.poset.prepare_unit(new_unit) 63 | assert process.poset.check_compliance(new_unit), "A unit created by this process is not passing the compliance test!" 64 | process.sign_unit(new_unit) 65 | process.add_unit_to_poset(new_unit) 66 | 67 | for process_id in range(n_processes): 68 | if process_id != creator_id: 69 | hashes_to_units = processes[process_id].poset.units 70 | U = translate_parents_and_copy(new_unit, hashes_to_units) 71 | processes[process_id].add_unit_to_poset(U) 72 | break 73 | 74 | if unit_no%50 == 0: 75 | print(f"Adding unit no {unit_no} out of {n_units}.") 76 | -------------------------------------------------------------------------------- /tests/linear_ordering_network.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | import asyncio 15 | import socket 16 | 17 | from aleph.network import tx_source_gen 18 | from aleph.process import Process 19 | from aleph.crypto.keys import SigningKey, VerifyKey 20 | 21 | import aleph.const as consts 22 | 23 | 24 | async def main(): 25 | n_processes = 20 26 | consts.USE_TCOIN = 0 27 | consts.UNITS_LIMIT = None 28 | consts.LEVEL_LIMIT = 100 29 | consts.N_PARENTS = n_processes 30 | 31 | processes = [] 32 | host_ports = [8900+i for i in range(n_processes)] 33 | local_ip = socket.gethostbyname(socket.gethostname()) 34 | addresses = [(local_ip, port) for port in host_ports] 35 | recv_addresses = [(local_ip, 9100+i) for i in range(n_processes)] 36 | 37 | signing_keys = [SigningKey() for _ in range(n_processes)] 38 | public_keys = [VerifyKey.from_SigningKey(sk) for sk in signing_keys] 39 | 40 | tasks = [] 41 | userDB = None 42 | 43 | for process_id in range(n_processes): 44 | sk = signing_keys[process_id] 45 | pk = public_keys[process_id] 46 | new_process = Process(n_processes, 47 | process_id, 48 | sk, pk, 49 | addresses, 50 | public_keys, 51 | None, 52 | userDB, 53 | tx_source_gen(batch_size=3, txpu=1, seed=process_id)) 54 | processes.append(new_process) 55 | tasks.append(asyncio.create_task(new_process.run())) 56 | 57 | await asyncio.gather(*tasks) 58 | 59 | 60 | if __name__ == '__main__': 61 | asyncio.run(main()) 62 | -------------------------------------------------------------------------------- /tests/make_keys.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | #!/usr/bin/env python3 15 | from aleph.crypto.keys import SigningKey, VerifyKey 16 | 17 | def save_keys(keys, filename): 18 | with open(filename, "w") as the_file: 19 | for key in keys: 20 | the_file.write(key.decode('utf8')+"\n") 21 | 22 | def save_priv_keys(priv_keys, i): 23 | save_keys(priv_keys, "key" + str(i) + ".secret") 24 | 25 | def save_pub_keys(pub_keys): 26 | save_keys(pub_keys, "keys.public") 27 | 28 | if __name__ == '__main__': 29 | import sys 30 | assert len(sys.argv) == 3 31 | n_machines = int(sys.argv[1]) 32 | processes_per_machine = int(sys.argv[2]) 33 | pub_keys = [] 34 | for i in range(n_machines): 35 | priv_keys = [] 36 | for _ in range(processes_per_machine): 37 | priv_key = SigningKey() 38 | pub_keys.append(VerifyKey.from_SigningKey(priv_key).to_hex()) 39 | priv_keys.append(priv_key.to_hex()) 40 | save_priv_keys(priv_keys, i) 41 | save_pub_keys(pub_keys) 42 | -------------------------------------------------------------------------------- /tests/read_dumped_poset.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils import dag_utils 15 | import time, os, sys 16 | import random 17 | 18 | # path to a file with a dag dumped from poset using the dump_to_file() method 19 | file_name = 'poset_1.dag' 20 | if len(sys.argv) > 1: 21 | file_name = sys.argv[1] 22 | 23 | if not os.path.exists(file_name): 24 | print(f"The file {file_name} doesn't exist. Aborting.") 25 | exit(0) 26 | 27 | 28 | dag = dag_utils.dag_from_file(file_name) 29 | print(f"Dag consists of {len(dag)} units.") 30 | # retrieve the list of units in the same order as they were added to the poset 31 | units_list = dag.get_node_list_as_added() 32 | 33 | print("level (pr. units) (min pr. units) (avg n vis. below)") 34 | for level in range(1000): 35 | primes = dag.get_prime_units_by_level(level) 36 | if primes == []: 37 | break 38 | min_primes = [U for U in primes if all(dag.level(V) < dag.level(U) for V in dag.parents(U))] 39 | 40 | n_visible_below = [] 41 | for U in primes: 42 | below = dag.get_prime_units_by_level(level-1) 43 | cnt = sum(dag.is_reachable(V, U) for V in below) 44 | n_visible_below.append(cnt) 45 | 46 | avg_n_visible = sum(n_visible_below)/len(n_visible_below) 47 | print(f"{level: <15} {len(primes): <15} {len(min_primes): <15} {avg_n_visible:<15.2f}") 48 | 49 | 50 | for U in units_list: 51 | parents = dag.parents(U) 52 | if parents != []: 53 | self_predecessor = parents[0] 54 | level_now = dag.level(U) 55 | level_prev = dag.level(self_predecessor) 56 | if level_now - level_prev > 1: 57 | creator_id = dag.pid(U) 58 | print(f"Process {creator_id} jumped from level {level_prev} to {level_now}.") 59 | 60 | 61 | tries = 10**4 62 | timer_start = time.time() 63 | for _ in range(tries): 64 | U1 = random.choice(units_list) 65 | U2 = random.choice(units_list) 66 | res = dag.is_reachable(U1, U2) 67 | 68 | timer_stop = time.time() 69 | print(f"Executed {tries} random below queries, total time {timer_stop - timer_start:.4f}") 70 | 71 | for U in units_list: 72 | if dag.get_node_info(U, "timing"): 73 | print(U, "is the timing unit at level", dag.level(U)) 74 | 75 | -------------------------------------------------------------------------------- /tests/threshold_coin_distribution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a Proof-of-Concept implementation of Aleph Zero consensus protocol. 3 | Copyright (C) 2019 Aleph Zero Team 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | GNU General Public License for more details. 9 | 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see . 12 | ''' 13 | 14 | from aleph.utils.generic_test import simulate_with_checks 15 | import random 16 | from joblib import Parallel, delayed 17 | from time import time 18 | 19 | def report_coin_toss(U, poset, dag, results, additional_args): 20 | if additional_args is None: 21 | levels, U_c = set(), None 22 | else: 23 | levels, U_c = additional_args 24 | if U.level not in levels: 25 | levels.add(U.level) 26 | if U.level == 3: 27 | U_c = U 28 | if U_c is not None and U.level-U_c.level >= 4: 29 | results.append(poset.toss_coin(U_c, U)) 30 | return levels, U_c 31 | 32 | 33 | def check_distribution(n_jobs, n_processes, n_units, n_forkers): 34 | print('n_jobs', n_jobs, '\nn_processes', n_processes, '\nn_units', n_units, '\nn_forkers', n_forkers) 35 | print('dispatching workers') 36 | start = time() 37 | results = Parallel(n_jobs=n_jobs)( 38 | delayed(simulate_with_checks)( 39 | n_processes, 40 | n_units, 41 | n_forkers, 42 | post_prepare = report_coin_toss, 43 | use_tcoin = True, 44 | seed = round(time())+i 45 | ) for i in range(n_jobs)) 46 | print('work done in', round(time()-start,2)) 47 | 48 | all_sum, all_len = 0, 0 49 | for res in results: 50 | all_sum += sum(res) 51 | all_len += len(res) 52 | if all_len: 53 | print(all_len, all_sum/all_len) 54 | 55 | 56 | if __name__ == '__main__': 57 | check_distribution(8, 16, 4000, 0) 58 | --------------------------------------------------------------------------------