diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93794e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,79 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.serpython +python +venv \ No newline at end of file diff --git a/carnot/spec.md b/carnot/spec.md index ef6de5d..c5831ac 100644 --- a/carnot/spec.md +++ b/carnot/spec.md @@ -1,25 +1,21 @@ -Psuedocode specification of the Carnot consensus algorithm. +# Carnot Specification +This is the pseudocode specification of the Carnot consensus algorithm. In this specification we will omit any cryptographic material, block validity and proof checks. A real implementation is expected to check those before hitting this code. In addition, all types can be expected to have their invariants checked by the type contract, e.g. in an instance of type `Qc::Aggregate` the `high_qc` field is always the most recent qc among the aggregate qcs and the code can skip this check. 'Q:' is used to indicate unresolved questions. Notation is loosely based on CDDL. - - ## Messages A critical piece in the protocol, these are the different kind of messages used by participants during the protocol execution. * `Block`: propose a new block * `Vote`: vote for a block proposal -* `TimeoutMsg`: propose to jump to a new (next) view after a proposal for the current one was not received before a configurable timeout. +* `Timeout`: propose to jump to a new view after a proposal for the current one was not received before a configurable timeout. ### Block + (sometimes also called proposal) -``` -view: View -qc: Qc -``` We assume an unique identifier of the block can be obtained, for example by hashing its contents. We will use the `id()` function to access the identifier of the current block. We also assume that a unique tree order of blocks can be determined, and in particular each participant can identify the parent of each block. We will use the `parent()` function to access such parent block. @@ -31,51 +27,37 @@ class Block: ``` ##### View -``` -view_n: uint -``` -a monotonically increasing number (considerations about the size?) + +A monotonically increasing number (considerations about the size?) ```python View = int ``` ##### Qc -There are currently two different types of QC: -``` -Qc = Standard / Aggregate -``` +There are currently two different types of QC: ```python Qc = StandardQc | AggregateQc ``` ###### Standard -``` -view: View -block: Id -``` + Q: there can only be one block on which consensus in achieved for a view, so maybe the block field is redundant? ```python - -@dataclass class StandardQc: view: View block: Id ``` ###### Aggregate -``` -view: View -high_qc: Qc -``` + `high_qc` is `Qc` for the most recent view among the aggregated ones. The rest of the qcs are ignored in the rest of this algorithm. We assume there is a `block` function available that returns the block for the Qc. In case of a standard qc, this is trivially qc.block, while for aggregate it can be obtained by accessing `high_qc`. `high_qc` is guaranteed to be a 'Standard' qc. ```python -@dataclass class AggregateQc: view: View qcs: List[Qc] @@ -88,21 +70,15 @@ class AggregateQc: undef, will assume a 32-byte opaque string ```python -Id = bytearray +Id: bytes = bytearray(32) ``` ### Vote + A vote for `block` in `view` -``` -block: Id -view: View -voter: Id -? qc: Qc -``` qc is the optional field containing the QC built by root nodes from 2/3 + 1 votes from their child committees and forwarded the the next view leader. -# While adding votes, timeout msgs etc, make sure duplicate msgs are discarded. + ```python -@dataclass class Vote: block: Id view: View @@ -127,6 +103,12 @@ class TimeoutMsg: timeout_qc: Qc sender: Id +### Timeout + +```python +class Timeout: + view: View + high_qc: Qc ``` ## Local Variables @@ -190,9 +172,11 @@ The following functions are expected to be available to participants during the ## Core functions + These are the core functions necessary for the Carnot consensus protocol, to be executed in response of incoming messages, except for `timeout` which is triggered by a participant configurable timer. ### Receive block + ```python3 def receive_block(block: Block): if block.id() is known or block.view <=LATEST_COMMITTED_VIEW: @@ -217,12 +201,12 @@ def receive_block(block: Block): else: send(vote, parent_commitee()) current_view += 1 - reset_timer(current_view) # needs to be updated after pacemaker is completed. + reset_timer() try_to_commit_grandparent(block) ``` ##### Auxiliary functions -```python +```python def safe_block(block: Block): match block.qc: case StandardQc() as standard: @@ -279,7 +263,9 @@ def update_high_qc(qc: Qc): ``` ### Receive Vote + Q: this whole function needs to be revised + ```python def receive_vote(vote: Vote):