Skip to content

Instantly share code, notes, and snippets.

@spikeekips
Last active February 13, 2018 02:38
Show Gist options
  • Select an option

  • Save spikeekips/ebf41bc1270ab6c8d7c354df267f73b4 to your computer and use it in GitHub Desktop.

Select an option

Save spikeekips/ebf41bc1270ab6c8d7c354df267f73b4 to your computer and use it in GitHub Desktop.

Revisions

  1. spikeekips revised this gist Feb 13, 2018. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -44,6 +44,8 @@ Simply to say, `commons` is the shared nodes between quorums.
    >
    > Definition (safety). A set of nodes in an FBAS enjoy safety if no two of them ever externalize different values for the same slot.
    - From [The Stellar Consensus Protocol](https://www.stellar.org/papers/stellar-consensus-protocol.pdf)

    So we can simply get the number of nodes for satisfying `safety`.

    ```
    @@ -57,6 +59,8 @@ So we can simply get the number of nodes for satisfying `safety`.
    >
    > Definition (liveness). A node in an FBAS enjoys liveness if it can externalize new values without the participation of any failed (including ill-behaved) nodes.
    - From [The Stellar Consensus Protocol](https://www.stellar.org/papers/stellar-consensus-protocol.pdf)

    `liveness` also can be calculated,

    ```
    @@ -155,4 +159,4 @@ $ python check-quorums-are-stable.py 5 4 2
    ............................................................................................................................................................................................................................................
    liveness(a): {"FAULT": 3, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 3, "QUORUM": "b", "commons": 2, "has_liveness": false, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    ```
    ```
  2. spikeekips revised this gist Feb 13, 2018. 1 changed file with 89 additions and 18 deletions.
    107 changes: 89 additions & 18 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,72 @@
    # Checking The Stability Of Two Quorums

    This script will simply do these,

    * set up and compose the 2 quorums automatically with given numbers, and the `commons`(shared nodes between 2 quorums) is also considered.
    * by the possible number of faulty nodes, checking and calculating these factors,
    * `safety`
    * `liveness`
    * minimum `threshold`

    I want to get the possible cases and by increasing the number of faulty nodes, how each factors are changed.

    ## Terminology

    ### `node`

    In this script, node will be limited to 'validator', which can participate to consensus process of network.


    ### `quorum`

    The paper, [The Stellar Consensus Protocol](https://www.stellar.org/papers/stellar-consensus-protocol.pdf) by David Mazie`res, explains like this,

    > Definition (quorum). A set of nodes `𝑈 ⊆ 𝐕` in FBAS `⟨𝐕,𝐐⟩` is a quorum iff `𝑈 ≠ ∅` and `𝑈` contains a slice for each member — i.e., `∀𝑣 ∈ 𝑈`, `∃𝑞 ∈ 𝐐(𝑣)` such that `𝑞 ⊆ 𝑈`.
    > A quorum is a set of nodes sufficient to reach agreement.
    `quorum slice` is the sufficient set of nodes for each node, but `quorum` is for the network-level.


    ### `commons`

    According to the [official documentation](https://www.stellar.org/developers/stellar-core/software/admin.html#quorum-intersection) of stellar.org,

    > For example, consider two nodes that respectively reference the sets Set1 and Set2 composed of some common nodes and some other nodes.
    >
    > Set1 = Common + extra1, Set2 = Common + extra2
    Simply to say, `commons` is the shared nodes between quorums.

    ### `safety`

    > 3.3. Safety and liveness
    >
    > Definition (safety). A set of nodes in an FBAS enjoy safety if no two of them ever externalize different values for the same slot.
    So we can simply get the number of nodes for satisfying `safety`.

    ```
    <commons> - 1 >= f
    ```
    `f` is the number of faulty nodes, that is, safety nodes will be greater than faulty nodes.

    ### `liveness`

    > 3.3. Safety and liveness
    >
    > Definition (liveness). A node in an FBAS enjoys liveness if it can externalize new values without the participation of any failed (including ill-behaved) nodes.
    `liveness` also can be calculated,

    ```
    <nodes in one quorum> - <commons> >= f
    ```

    ### `threshold`

    `threshold` is the lowest number to reach agreement in one quorum.

    ## Installation

    ```
    @@ -8,36 +77,38 @@ $ pip install pygments termcolor

    ```
    $ python check-quorums-are-stable.py -h
    usage: check-quorums-are-stable.py [-h] na nb commons
    usage: check-quorums-are-stable.py [-h] qa qb commons
    Checking the stability of 2 quorums
    positional arguments:
    na number of nodes in quorum `a`
    nb number of nodes in quorum `b`
    commons number of common nodes
    qa number of nodes in quorum, `a`
    qb number of nodes in quorum, `b`
    commons number of commons(shared nodes) between quorums, `a` and `b`
    optional arguments:
    -h, --help show this help message and exit
    ```

    ## Check Quorums
    ## Check Stability Of Quorums

    ```
    $ python check-quorums-are-stable.py 5 4 2
    - quorums ---------------------------------------------------------------------------------------------
    - quorums ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    a: {"count": 5, "quorum": ["v00", "v01", "v02", "v03", "v04"]}
    b: {"count": 4, "quorum": ["v03", "v04", "v05", "v06"]}
    commons: {"commons": ["v03", "v04"], "count": 2}
    - functions -------------------------------------------------------------------------------------------
    - functions --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    fault tolerance: {"f": "<number of nodes> >= 3f + 1"}
    max safety: {"f": "<commons> - 1 >= f"}
    liveness: {"f": "<size of nodes> - <commons> >= f"}
    threshold: {"f": "<size of not commons> + <safety> / <size of nodes>: https://www.stellar.org/developers/stellar-core/software/admin.html#quorum-intersection"}
    or threshold: {"f": "1 - <commons p> + (<safety p> * <common p>)"}
    - etc -------------------------------------------------------------------------------------------------
    - etc --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    max fault: {"fault": 1}
    max safety: {"safety": 1}
    =======================================================================================================
    - FAULT: 0 --------------------------------------------------------------------------------------------
    ============================================================================================================================================================================================================================================
    - FAULT: 0 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 0, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    @@ -49,11 +120,11 @@ $ python check-quorums-are-stable.py 5 4 2
    safety(all): {"FAULT": 0, "is_safety": true, "max_safety": 1, "safe_nodes": 1, "safety": 0}
    min threshold(a): {"FAULT": 0, "QUORUM": "a", "commons": 2, "commons_p": 0.4, "max_safety": 1, "min_threshold": 0.8, "safe_nodes": 1, "safety_p": 0.5}
    min threshold(b): {"FAULT": 0, "QUORUM": "b", "commons": 2, "commons_p": 0.5, "max_safety": 1, "min_threshold": 0.75, "safe_nodes": 1, "safety_p": 0.5}
    .......................................................................................................
    ............................................................................................................................................................................................................................................
    liveness(a): {"FAULT": 0, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": true, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 0, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": true, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 1 --------------------------------------------------------------------------------------------
    - FAULT: 1 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 1, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    @@ -63,25 +134,25 @@ $ python check-quorums-are-stable.py 5 4 2
    * safe_nodes: 1
    safety(all): {"FAULT": 1, "is_safety": false, "max_safety": 1, "safe_nodes": 1, "safety": 0}
    .......................................................................................................
    ............................................................................................................................................................................................................................................
    liveness(a): {"FAULT": 1, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 1, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 2 --------------------------------------------------------------------------------------------
    - FAULT: 2 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 2, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 2, "is_safety": false, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    .......................................................................................................
    ............................................................................................................................................................................................................................................
    liveness(a): {"FAULT": 2, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 2, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 3 --------------------------------------------------------------------------------------------
    - FAULT: 3 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 3, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 3, "is_safety": false, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    .......................................................................................................
    ............................................................................................................................................................................................................................................
    liveness(a): {"FAULT": 3, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 3, "QUORUM": "b", "commons": 2, "has_liveness": false, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    ```
    ```
  3. spikeekips revised this gist Feb 12, 2018. 1 changed file with 87 additions and 0 deletions.
    87 changes: 87 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    ## Installation

    ```
    $ pip install pygments termcolor
    ```

    ## Usage

    ```
    $ python check-quorums-are-stable.py -h
    usage: check-quorums-are-stable.py [-h] na nb commons
    positional arguments:
    na number of nodes in quorum `a`
    nb number of nodes in quorum `b`
    commons number of common nodes
    optional arguments:
    -h, --help show this help message and exit
    ```

    ## Check Quorums

    ```
    $ python check-quorums-are-stable.py 5 4 2
    - quorums ---------------------------------------------------------------------------------------------
    a: {"count": 5, "quorum": ["v00", "v01", "v02", "v03", "v04"]}
    b: {"count": 4, "quorum": ["v03", "v04", "v05", "v06"]}
    commons: {"commons": ["v03", "v04"], "count": 2}
    - functions -------------------------------------------------------------------------------------------
    fault tolerance: {"f": "<number of nodes> >= 3f + 1"}
    max safety: {"f": "<commons> - 1 >= f"}
    liveness: {"f": "<size of nodes> - <commons> >= f"}
    threshold: {"f": "<size of not commons> + <safety> / <size of nodes>: https://www.stellar.org/developers/stellar-core/software/admin.html#quorum-intersection"}
    or threshold: {"f": "1 - <commons p> + (<safety p> * <common p>)"}
    - etc -------------------------------------------------------------------------------------------------
    max fault: {"fault": 1}
    max safety: {"safety": 1}
    =======================================================================================================
    - FAULT: 0 --------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 0, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 0, "is_safety": true, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    min threshold(a): {"FAULT": 0, "QUORUM": "a", "commons": 2, "commons_p": 0.4, "max_safety": 1, "min_threshold": 1.0, "safe_nodes": 2, "safety_p": 1.0}
    min threshold(b): {"FAULT": 0, "QUORUM": "b", "commons": 2, "commons_p": 0.5, "max_safety": 1, "min_threshold": 1.0, "safe_nodes": 2, "safety_p": 1.0}
    * safe_nodes: 1
    safety(all): {"FAULT": 0, "is_safety": true, "max_safety": 1, "safe_nodes": 1, "safety": 0}
    min threshold(a): {"FAULT": 0, "QUORUM": "a", "commons": 2, "commons_p": 0.4, "max_safety": 1, "min_threshold": 0.8, "safe_nodes": 1, "safety_p": 0.5}
    min threshold(b): {"FAULT": 0, "QUORUM": "b", "commons": 2, "commons_p": 0.5, "max_safety": 1, "min_threshold": 0.75, "safe_nodes": 1, "safety_p": 0.5}
    .......................................................................................................
    liveness(a): {"FAULT": 0, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": true, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 0, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": true, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 1 --------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 1, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 1, "is_safety": true, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    min threshold(a): {"FAULT": 1, "QUORUM": "a", "commons": 2, "commons_p": 0.4, "max_safety": 1, "min_threshold": 0.8, "safe_nodes": 1, "safety_p": 0.5}
    min threshold(b): {"FAULT": 1, "QUORUM": "b", "commons": 2, "commons_p": 0.5, "max_safety": 1, "min_threshold": 0.75, "safe_nodes": 1, "safety_p": 0.5}
    * safe_nodes: 1
    safety(all): {"FAULT": 1, "is_safety": false, "max_safety": 1, "safe_nodes": 1, "safety": 0}
    .......................................................................................................
    liveness(a): {"FAULT": 1, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 1, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 2 --------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 2, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 2, "is_safety": false, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    .......................................................................................................
    liveness(a): {"FAULT": 2, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 2, "QUORUM": "b", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    - FAULT: 3 --------------------------------------------------------------------------------------------
    max_safety(all): {"FAULT": 3, "commons": 2, "max_safety": 1}
    * safe_nodes: 2
    safety(all): {"FAULT": 3, "is_safety": false, "max_safety": 1, "safe_nodes": 2, "safety": 1}
    .......................................................................................................
    liveness(a): {"FAULT": 3, "QUORUM": "a", "commons": 2, "has_liveness": true, "is_safety": false, "max_safety": 1, "size_of_nodes": 5}
    liveness(b): {"FAULT": 3, "QUORUM": "b", "commons": 2, "has_liveness": false, "is_safety": false, "max_safety": 1, "size_of_nodes": 4}
    ```
  4. spikeekips created this gist Feb 12, 2018.
    193 changes: 193 additions & 0 deletions check-quorums-are-stable.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,193 @@
    import argparse
    import collections
    import json
    import os
    import pygments
    import pygments.lexers
    import pygments.formatters
    from termcolor import colored, cprint
    import math
    import sys # noqa


    _, _TERMINAL_COLUMNS = os.popen('stty size', 'r').read().split()
    TERMINAL_COLUMNS = int(_TERMINAL_COLUMNS)


    def print_line(h, c='-', color=None):
    if h is None:
    print(c * TERMINAL_COLUMNS)

    return

    s = h
    if color is not None:
    s = colored(h, color=color)

    print(c, s, c * (TERMINAL_COLUMNS - len(h) - 3))

    return


    def print_metric(h, **kw):
    color = None
    for k, v in kw.copy().items():
    if type(v) in (float,):
    kw[k] = float('%.2f' % v)

    continue

    if k.startswith('_is_') or k.startswith('_has_'):
    kw[k[1:]] = v
    del kw[k]
    elif not k.startswith('is_') and not k.startswith('has_'):
    continue

    if not v:
    color = 'red'

    formatted_json = json.dumps(kw, sort_keys=True)

    if h is not None and len(h) > 0:
    print('%20s:' % h, end=' ')

    if color is None:
    colorful_json = pygments.highlight(
    formatted_json,
    pygments.lexers.JsonLexer(),
    pygments.formatters.TerminalFormatter(),
    )
    print(colorful_json, end='')

    return

    cprint(formatted_json, color)

    return


    def check_quorums(a, b):
    qs = collections.namedtuple('QuorumSet', ('a', 'b'))(set(a), set(b))
    commons = qs.a & qs.b

    print_line('quorums')
    print_metric('a', count=len(qs.a), quorum=sorted(qs.a))
    print_metric('b', count=len(qs.b), quorum=sorted(qs.b))
    print_metric('commons', count=len(commons), commons=sorted(commons))

    print_line('functions')
    print_metric('fault tolerance', f='<number of nodes> >= 3f + 1')
    print_metric('max safety', f='<commons> - 1 >= f')
    print_metric('liveness', f='<size of nodes> - <commons> >= f')
    print_metric(
    'threshold',
    f='<size of not commons> + <safety> / <size of nodes>: https://www.stellar.org/developers/stellar-core/software/admin.html#quorum-intersection', # noqa
    )
    print_metric('or threshold', f='1 - <commons p> + (<safety p> * <common p>)')

    print_line('etc')
    max_f = min(math.ceil((len(q0) - 1) / 3), math.ceil((len(q1) - 1) / 3))
    print_metric('max fault', fault=max_f)
    print_metric('max safety', safety=len(commons) - max_f)

    print_line(None, c='=')

    f = 0
    while True:
    print_line('FAULT: %d' % f, color='green')

    # check safety
    max_safety = len(commons) - 1

    print_metric(
    'max_safety(all)',
    max_safety=max_safety,
    FAULT=f,
    commons=len(commons),
    )

    safe_nodes = max_safety + 1
    while safe_nodes >= 1:
    print()

    cprint('%3s* safe_nodes: %d' % ('', safe_nodes), 'magenta')
    is_safety = safe_nodes - 1 >= f

    print_metric(
    'safety(all)',
    safe_nodes=safe_nodes,
    safety=safe_nodes - 1,
    FAULT=f,
    is_safety=is_safety,
    max_safety=max_safety,
    )

    if not is_safety:
    break

    if safe_nodes - f >= 0:
    for name in ('a', 'b'):
    q = getattr(qs, name)

    safetyP = (safe_nodes - f) / len(commons)
    commonP = len(commons) / len(q)
    min_threshold = 1 - (1 - safetyP) * commonP

    print_metric(
    'min threshold(%s)' % name,
    min_threshold=min_threshold,
    safe_nodes=safe_nodes - f,
    commons=len(commons),
    safety_p=safetyP,
    commons_p=commonP,
    QUORUM=name,
    max_safety=max_safety,
    FAULT=f,
    )

    safe_nodes -= 1

    print_line(None, c='.')
    for name in ('a', 'b'):
    q = getattr(qs, name)

    liveness_fault = len(q) - len(commons)
    has_liveness = liveness_fault >= f

    print_metric(
    'liveness(%s)' % name,
    has_liveness=has_liveness,
    _is_safety=is_safety,
    size_of_nodes=len(q),
    commons=len(commons),
    FAULT=f,
    QUORUM=name,
    max_safety=max_safety,
    )

    if not is_safety and not has_liveness:
    break

    f += 1
    print()

    return


    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('na', type=int, help='number of nodes in quorum `a`')
    parser.add_argument('nb', type=int, help='number of nodes in quorum `b`')
    parser.add_argument('commons', type=int, help='number of common nodes')


    if __name__ == '__main__':
    options = parser.parse_args()

    q0 = tuple(map(lambda x: 'v%02d' % x, range(options.na)))
    q1 = tuple(map(
    lambda x: 'v%02d' % x,
    range(options.na - options.commons, options.na - options.commons + options.nb),
    ))

    check_quorums(q0, q1)