def test_decoder_selection(mocked_cpu_count, galois_field):
    # Very small n < 8. Vandermonde should always be picked
    point = EvalPoint(galois_field, 4, use_omega_powers=True)
    for cpu_count in [1, 100]:
        mocked_cpu_count.return_value = cpu_count
        for batch_size in [1, 1000, 100000]:
            DecoderSelector.set_optimal_thread_count(batch_size)
            assert isinstance(
                DecoderSelector.select(point, batch_size), VandermondeDecoder
            )

    # Small batches (~n). Reasonable number of threads. Pick FFT
    point = EvalPoint(galois_field, 65, use_omega_powers=True)
    for cpu_count in [1, 2, 4, 8]:
        mocked_cpu_count.return_value = cpu_count
        for batch_size in [1, 16, 32]:
            DecoderSelector.set_optimal_thread_count(batch_size)
            assert isinstance(DecoderSelector.select(point, batch_size), FFTDecoder)

    # Small n. Reasonable number of threads,
    # Large batch sizes > ~ numthreads * n. Pick Vandermonde
    point = EvalPoint(galois_field, 65, use_omega_powers=True)
    for cpu_count in [1, 2, 4, 8]:
        mocked_cpu_count.return_value = cpu_count
        for batch_size in [512, 1024, 2048, 4096]:
            DecoderSelector.set_optimal_thread_count(batch_size)
            assert isinstance(
                DecoderSelector.select(point, batch_size), VandermondeDecoder
            )

    # Extremely large n. FFT should ideally be picked at reasonable batch sizes
    point = EvalPoint(galois_field, 65536, use_omega_powers=True)
    for cpu_count in [1, 2, 4, 8]:
        mocked_cpu_count.return_value = cpu_count
        for batch_size in [512, 1024, 2048, 4096, 8192]:
            DecoderSelector.set_optimal_thread_count(batch_size)
            assert isinstance(DecoderSelector.select(point, batch_size), FFTDecoder)

    # The scenarios above checked extreme cases. Below we check more rigorously based
    # on the exact approximation formula. Ideally, the cases above should not change
    # over time but the tests below will as the selection algorithm changes
    for n in [32, 64, 128, 256]:
        point = EvalPoint(galois_field, n, use_omega_powers=True)
        for cpu_count in [2 ** i for i in range(5)]:
            mocked_cpu_count.return_value = cpu_count
            for batch_size in [2 ** i for i in range(16)]:
                DecoderSelector.set_optimal_thread_count(batch_size)
                if batch_size > 0.5 * n * min(batch_size, AvailableNTLThreads()):
                    assert isinstance(
                        DecoderSelector.select(point, batch_size), VandermondeDecoder
                    )
                else:
                    assert isinstance(
                        DecoderSelector.select(point, batch_size), FFTDecoder
                    )
Example #2
0
async def test_randousha(test_router, polynomial, galois_field, n, k):
    t = (n - 1) // 3
    sends, receives, _ = test_router(n)
    shares_per_party = await asyncio.gather(*[
        randousha(n, t, k, i, sends[i], receives[i], galois_field)
        for i in range(n)
    ])
    assert len(shares_per_party) == n
    assert all(
        len(random_shares) == (n - 2 * t) * k
        for random_shares in shares_per_party)
    random_values = []
    eval_point = EvalPoint(galois_field, n, use_omega_powers=False)
    decoder = DecoderFactory.get(eval_point, Algorithm.VANDERMONDE)
    for i, shares in enumerate(zip(*shares_per_party)):
        shares_t, shares_2t = zip(*shares)
        poly_t = polynomial(decoder.decode(list(range(n)), shares_t))
        poly_2t = polynomial(decoder.decode(list(range(n)), shares_2t))
        r_t = poly_t(0)
        r_2t = poly_2t(0)
        assert len(poly_t.coeffs) == t + 1
        assert len(poly_2t.coeffs) == 2 * t + 1
        assert r_t == r_2t
        random_values.append(r_t)
    assert len(set(random_values)) == (n - 2 * t) * k
def test_encoder_selection(galois_field):
    # Very small n < 8. Vandermonde should always be picked
    point = EvalPoint(galois_field, 4, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), VandermondeEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), VandermondeEncoder)

    # Intermediate values of n (8 < n < 128
    # Bad n (Nearest power of 2 is much higher) Vandermonde should
    # always be picked
    point = EvalPoint(galois_field, 65, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), VandermondeEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), VandermondeEncoder)

    point = EvalPoint(galois_field, 40, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), VandermondeEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), VandermondeEncoder)

    # Good n (Nearest power of 2 is close) FFT should be picked
    point = EvalPoint(galois_field, 120, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), FFTEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), FFTEncoder)

    point = EvalPoint(galois_field, 55, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), FFTEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), FFTEncoder)

    # Large n. Always pick FFT
    point = EvalPoint(galois_field, 255, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), FFTEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), FFTEncoder)

    point = EvalPoint(galois_field, 257, use_omega_powers=True)
    assert isinstance(EncoderSelector.select(point, 1), FFTEncoder)
    assert isinstance(EncoderSelector.select(point, 100000), FFTEncoder)
Example #4
0
 async def _get_inputmask(self, idx):
     # Private reconstruct
     contract_concise = ConciseContract(self.contract)
     n = contract_concise.n()
     poly = polynomials_over(field)
     eval_point = EvalPoint(field, n, use_omega_powers=False)
     shares = []
     for i in range(n):
         share = self.req_mask(i, idx)
         shares.append(share)
     shares = await asyncio.gather(*shares)
     shares = [(eval_point(i), share) for i, share in enumerate(shares)]
     mask = poly.interpolate_at(shares, 0)
     return mask
def fft_decoding_test_cases(galois_field):
    point = EvalPoint(galois_field, 4, use_omega_powers=True)
    omega = point.omega.value
    p = galois_field.modulus
    test_cases = []
    test_cases.append(
        [
            [1, 3],
            [(2 * pow(omega, 1, p) + 1) % p, (2 * pow(omega, 3, p) + 1) % p],
            [1, 2],
            point,
        ]
    )
    return test_cases
def robust_decoding_test_cases(galois_field):
    omega = EvalPoint(galois_field, 4, use_omega_powers=True).omega.value
    p = galois_field.modulus
    # Order: Index of parties, Encoded values, Expected decoded values,
    # Expected erroneous parties, t, point
    test_cases = [
        # Correct array would be [3, 5, 7, 9]
        [[0, 1, 2, 3], [3, 5, 0, 9], [1, 2], [2], 1, EvalPoint(galois_field, 4)],
        [
            [0, 1, 2, 3],
            [
                (2 * pow(omega, 0, p) + 1) % p,
                (2 * pow(omega, 1, p) + 1) % p,
                0,
                (2 * pow(omega, 3, p) + 1) % p,
            ],
            [1, 2],
            [2],
            1,
            EvalPoint(galois_field, 4, use_omega_powers=True),
        ],
    ]

    return test_cases
def refine_randoms(n, t, field, random_shares_int):
    assert 3 * t + 1 <= n

    # Number of nodes which have contributed values to this batch
    k = len(random_shares_int)
    assert k >= n - t and k <= n

    encoder = EncoderFactory.get(EvalPoint(field, n, use_omega_powers=True))

    # Assume these shares to be the coefficients of a random polynomial. The
    # refined shares are evaluations of this polynomial at powers of omega.
    output_shares_int = encoder.encode(random_shares_int)

    # Remove `t` shares since they might have been contributed by corrupt parties.
    return output_shares_int[:k - t]
def fft_reconstruction_input(galois_field):
    n = 4
    t = 1
    fp = galois_field
    p = fp.modulus
    point = EvalPoint(fp, n, use_omega_powers=True)
    omega = point.omega.value
    # x + 2, 3x + 4
    secret_shares = [
        (omega**0 + 2, 3 * omega**0 + 4),
        (omega**1 + 2, 3 * omega**1 + 4),
        (omega**2 + 2, 3 * omega**2 + 4),
        (omega**3 + 2, 3 * omega**3 + 4),
    ]
    secrets = [2, 4]
    return n, t, fp, p, omega, secret_shares, secrets
def test_benchmark_gao_robust_decode(benchmark, t, galois_field):
    n = 3 * t + 1
    galois_field = GF(Subgroup.BLS12_381)
    point = EvalPoint(galois_field, n)
    dec = GaoRobustDecoder(t, point)
    parties = [_ for _ in range(n)]
    poly = polynomials_over(galois_field)
    truepoly = poly.random(degree=t)
    faults = []
    while len(faults) < t:
        r = randint(0, n - 1)
        if r not in faults:
            faults.append(r)
    shares_with_faults = []
    for i in parties:
        if i in faults:
            shares_with_faults.append(int(galois_field.random()))
        else:
            shares_with_faults.append(int(truepoly(i + 1)))
    benchmark(dec.robust_decode, parties, shares_with_faults)
Example #10
0
async def randousha(n, t, k, my_id, _send, _recv, field):
    """
    Generates a batch of (n-2t)k secret sharings of random elements
    """
    poly = polynomials_over(field)
    eval_point = EvalPoint(field, n, use_omega_powers=False)
    big_t = n - (2 * t) - 1  # This is same as `T` in the HyperMPC paper.
    encoder = EncoderFactory.get(eval_point)

    # Pick k random elements
    def to_int(coeffs):
        return tuple(map(int, coeffs))

    my_randoms = [field.random() for _ in range(k)]

    # Generate t and 2t shares of the random element.
    coeffs_t = [to_int(poly.random(t, r).coeffs) for r in my_randoms]
    coeffs_2t = [to_int(poly.random(2 * t, r).coeffs) for r in my_randoms]
    unref_t = encoder.encode(coeffs_t)
    unref_2t = encoder.encode(coeffs_2t)

    subscribe_recv_task, subscribe = subscribe_recv(_recv)

    def _get_send_recv(tag):
        return wrap_send(tag, _send), subscribe(tag)

    # Start listening for my share of t and 2t shares from all parties.
    send, recv = _get_send_recv("H1")
    share_recv_task = asyncio.create_task(_recv_loop(n, recv))

    # Send each party their shares.
    to_send_t = transpose_lists(unref_t)
    to_send_2t = transpose_lists(unref_2t)
    for i in range(n):
        send(i, (to_send_t[i], to_send_2t[i]))

    # Wait until all shares are received.
    received_shares = await share_recv_task
    unrefined_t_shares, unrefined_2t_shares = zip(*received_shares)

    # Apply the hyper-invertible matrix.
    # Assume the unrefined shares to be coefficients of a polynomial
    # and then evaluate that polynomial at powers of omega.
    ref_t = encoder.encode(transpose_lists(list(unrefined_t_shares)))
    ref_2t = encoder.encode(transpose_lists(list(unrefined_2t_shares)))

    # Parties with id in [N-2t+1, N] need to start
    # listening for shares which they have to check.
    send, recv = _get_send_recv("H2")
    to_send_t = transpose_lists(ref_t)
    to_send_2t = transpose_lists(ref_2t)

    if my_id > big_t:
        share_chk_recv_task = asyncio.create_task(_recv_loop(n, recv))

    # Send shares of parties with id in [N-2t+1, N] to those parties.
    for i in range(big_t + 1, n):
        send(i, (to_send_t[i], to_send_2t[i]))

    # Parties with id in [N-2t+1, N] need to verify that the shares are in-fact correct.
    if my_id > big_t:
        shares_to_check = await share_chk_recv_task
        shares_t, shares_2t = zip(*shares_to_check)
        response = HyperInvMessageType.ABORT

        def get_degree(p):
            for i in range(len(p))[::-1]:
                if p[i] != 0:
                    return i
            return 0

        def get_degree_and_secret(shares):
            decoder = DecoderFactory.get(eval_point)
            polys = decoder.decode(list(range(n)),
                                   transpose_lists(list(shares)))
            secrets = [p[0] for p in polys]
            degrees = [get_degree(p) for p in polys]
            return degrees, secrets

        degree_t, secret_t = get_degree_and_secret(shares_t)
        degree_2t, secret_2t = get_degree_and_secret(shares_2t)

        # Verify that the shares are in-fact `t` and `2t` shared.
        # Verify that both `t` and `2t` shares of the same value.
        if (all(deg == t for deg in degree_t)
                and all(deg == 2 * t for deg in degree_2t)
                and secret_t == secret_2t):
            response = HyperInvMessageType.SUCCESS

        logging.debug(
            "[%d] Degree check: %s, Secret Check: %s",
            my_id,
            all(deg == t for deg in degree_t)
            and all(deg == 2 * t for deg in degree_2t),
            secret_t == secret_2t,
        )

    # Start listening for the verification response.
    send, recv = _get_send_recv("H3")
    response_recv_task = asyncio.create_task(
        _recv_loop(n - big_t - 1, recv, big_t + 1))

    # Send the verification response.
    if my_id > big_t:
        for i in range(n):
            send(i, response)

    responses = await response_recv_task
    subscribe_recv_task.cancel()

    # If any of [T+1, N] parties say that the shares are inconsistent then abort.
    if responses.count(HyperInvMessageType.SUCCESS) != n - big_t - 1:
        raise HoneyBadgerMPCError(
            "Aborting because the shares were inconsistent.")

    out_t = flatten_lists([s[:big_t + 1] for s in ref_t])
    out_2t = flatten_lists([s[:big_t + 1] for s in ref_2t])

    return tuple(zip(out_t, out_2t))
Example #11
0
def make_wb_encoder_decoder(n, k, p, point=None):
    """
    n: number of symbols to encode
    k: number of symbols in the message
        (k=t+1) where t is the degree of the polynomial
    """
    if not k <= n <= p:
        raise Exception(
            "Must have k <= n <= p but instead had (n,k,p) == (%r, %r, %r)" % (n, k, p)
        )
    t = k - 1  # degree of polynomial
    fp = GF(p)
    poly = polynomials_over(fp)

    # the message points correspond to polynomial evaluations
    # at either f(i) for convenience, or
    #    f( omega^i ) where omega. If omega is an n'th root of unity,
    # then we can do efficient FFT-based polynomial interpolations.
    if point is None or type(point) is not EvalPoint:
        point = EvalPoint(fp, n, use_omega_powers=False)

    # message is a list of integers at most p
    def encode(message):
        if not all(x < p for x in message):
            raise Exception(
                "Message is improperly encoded as integers < p. It was:\n%r" % message
            )
        assert len(message) == t + 1

        the_poly = poly(message)
        return [the_poly(point(i)) for i in range(n)]

    def solve_system(encoded_message, max_e, debug=False):
        """
        input: points in form (x,y)
        output: coefficients of interpolated polynomial

        due to Jeremy Kun
        https://jeremykun.com/2015/09/07/welch-berlekamp/
        """
        for e in range(max_e, 0, -1):
            e_num_vars = e + 1
            q_num_vars = e + k

            def row(i, a, b):
                return (
                    [b * a ** j for j in range(e_num_vars)]
                    + [-1 * a ** j for j in range(q_num_vars)]
                    + [0]
                )  # the "extended" part of the linear system

            system = [row(i, a, b) for (i, (a, b)) in enumerate(encoded_message)] + [
                [fp(0)] * (e_num_vars - 1) + [fp(1)] + [fp(0)] * (q_num_vars) + [fp(1)]
            ]  # ensure coefficient of x^e in E(x) is 1

            if debug:
                logging.debug("\ne is %r" % e)
                logging.debug("\nsystem is:\n\n")
                for row in system:
                    logging.debug("\t%r" % (row,))

            solution = some_solution(system, free_variable_value=1)
            e_ = poly([solution[j] for j in range(e + 1)])
            q_ = poly([solution[j] for j in range(e + 1, len(solution))])

            if debug:
                logging.debug("\nreduced system is:\n\n")
                for row in system:
                    logging.debug("\t%r" % (row,))

                logging.debug("solution is %r" % (solution,))
                logging.debug("Q is %r" % (q_,))
                logging.debug("E is %r" % (e_,))

            p_, remainder = q_.__divmod__(e_)
            if debug:
                logging.debug("P(x) = %r" % p_)
                logging.debug("r(x) = %r" % remainder)
            if remainder.is_zero():
                return q_, e_
        raise ValueError("found no divisors!")

    def decode(encoded_msg, debug=True):
        assert len(encoded_msg) == n
        c = sum(m is None for m in encoded_msg)  # number of erasures
        assert 2 * t + 1 + c <= n
        # e = ceil((n - c - t - 1) / 2) = ((n - c - t) // 2)
        e = (n - c - t) // 2
        if debug:
            logging.debug(f"n: {n} k: {k} t: {t} c: {c}")
            logging.debug(f"decoding with e: {e}")
            logging.debug(f"decoding with c: {c}")

        enc_m = [(point(i), m) for i, m in enumerate(encoded_msg) if m is not None]

        if e == 0:
            # decode with no errors
            p_ = poly.interpolate(enc_m)
            return p_.coeffs

        q_, e_ = solve_system(enc_m, max_e=e, debug=debug)
        p_, remainder = q_.__divmod__(e_)
        if not remainder.is_zero():
            raise Exception("Q is not divisibly by E!")
        return p_.coeffs

    return encode, decode, solve_system
def decoding_test_cases(galois_field):
    test_cases = [
        [[1, 3], [5, 9], [1, 2], EvalPoint(galois_field, 4)],
        [[1, 3], [[5, 9], [8, 14]], [[1, 2], [2, 3]], EvalPoint(galois_field, 4)],
    ]
    return test_cases
def encoding_test_cases(galois_field):
    test_cases = [
        [[1, 2], [3, 5, 7, 9], EvalPoint(galois_field, 4)],
        [[[1, 2], [2, 3]], [[3, 5, 7, 9], [5, 8, 11, 14]], EvalPoint(galois_field, 4)],
    ]
    return test_cases