mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-02-20 17:52:50 +00:00
Comments and minor factoring improvements and such
This commit is contained in:
parent
daad22d1b1
commit
43388ee711
@ -2,6 +2,8 @@
|
||||
Verify certain results against test vectors with well-known results.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TypeVar, Iterator, Awaitable, Callable
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
@ -17,16 +19,19 @@ from .util import cli, await_client_ready
|
||||
from allmydata.client import read_config
|
||||
from allmydata.util import base32
|
||||
|
||||
def digest(bs: bytes) -> str:
|
||||
return sha256(bs).hexdigest()
|
||||
|
||||
CONVERGENCE_SECRETS = [
|
||||
b"aaaaaaaaaaaaaaaa",
|
||||
b"bbbbbbbbbbbbbbbb",
|
||||
b"abcdefghijklmnop",
|
||||
b"hello world stuf",
|
||||
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
|
||||
sha256(b"Hello world").digest()[:16],
|
||||
digest(b"Hello world")[:16],
|
||||
]
|
||||
|
||||
ONE_KB = sha256(b"Hello world").digest() * 32
|
||||
ONE_KB = digest(b"Hello world") * 32
|
||||
assert len(ONE_KB) == 1024
|
||||
|
||||
OBJECT_DATA = [
|
||||
@ -48,26 +53,43 @@ ZFEC_PARAMS = [
|
||||
(101, 256),
|
||||
]
|
||||
|
||||
@mark.parametrize('convergence', CONVERGENCE_SECRETS)
|
||||
def test_convergence(convergence):
|
||||
@mark.parametrize('convergence_idx', range(len(CONVERGENCE_SECRETS)))
|
||||
def test_convergence(convergence_idx):
|
||||
"""
|
||||
Convergence secrets are 16 bytes.
|
||||
"""
|
||||
convergence = CONVERGENCE_SECRETS[convergence_idx]
|
||||
assert isinstance(convergence, bytes), "Convergence secret must be bytes"
|
||||
assert len(convergence) == 16, "Convergence secret must by 16 bytes"
|
||||
|
||||
|
||||
@mark.parametrize('data', OBJECT_DATA)
|
||||
def test_data(data):
|
||||
@mark.parametrize('data_idx', range(len(OBJECT_DATA)))
|
||||
def test_data(data_idx):
|
||||
"""
|
||||
Plaintext data is bytes.
|
||||
"""
|
||||
data = OBJECT_DATA[data_idx]
|
||||
assert isinstance(data, bytes), "Object data must be bytes."
|
||||
|
||||
@mark.parametrize('params', ZFEC_PARAMS)
|
||||
@mark.parametrize('convergence', CONVERGENCE_SECRETS)
|
||||
@mark.parametrize('data', OBJECT_DATA)
|
||||
@mark.parametrize('params_idx', range(len(ZFEC_PARAMS)))
|
||||
@mark.parametrize('convergence_idx', range(len(CONVERGENCE_SECRETS)))
|
||||
@mark.parametrize('data_idx', range(len(OBJECT_DATA)))
|
||||
@ensureDeferred
|
||||
async def test_chk_capability(reactor, request, alice, params, convergence, data):
|
||||
async def test_chk_capability(reactor, request, alice, params_idx, convergence_idx, data_idx):
|
||||
"""
|
||||
The CHK capability that results from uploading certain well-known data
|
||||
with certain well-known parameters results in exactly the previously
|
||||
computed value.
|
||||
"""
|
||||
params = ZFEC_PARAMS[params_idx]
|
||||
convergence = CONVERGENCE_SECRETS[convergence_idx]
|
||||
data = OBJECT_DATA[data_idx]
|
||||
|
||||
# rewrite alice's config to match params and convergence
|
||||
await reconfigure(reactor, request, alice, params, convergence)
|
||||
|
||||
# upload data as a CHK
|
||||
actual = upload_immutable(alice, data)
|
||||
actual = upload(alice, "chk", data)
|
||||
|
||||
# compare the resulting cap to the expected result
|
||||
expected = vectors.chk[key(params, convergence, data)]
|
||||
@ -82,18 +104,47 @@ async def asyncfoldr(
|
||||
f: Callable[[α, β], β],
|
||||
initial: β,
|
||||
) -> β:
|
||||
"""
|
||||
Right fold over an async iterator.
|
||||
|
||||
:param i: The async iterator.
|
||||
:param f: The function to fold.
|
||||
:param initial: The starting value.
|
||||
|
||||
:return: The result of the fold.
|
||||
"""
|
||||
result = initial
|
||||
async for a in i:
|
||||
result = f(a, result)
|
||||
return result
|
||||
|
||||
|
||||
def insert(item: tuple[α, β], d: dict[α, β]) -> dict[α, β]:
|
||||
"""
|
||||
In-place add an item to a dictionary.
|
||||
|
||||
If the key is already present, replace the value.
|
||||
|
||||
:param item: A tuple of the key and value.
|
||||
:param d: The dictionary to modify.
|
||||
|
||||
:return: The dictionary.
|
||||
"""
|
||||
d[item[0]] = item[1]
|
||||
return d
|
||||
|
||||
|
||||
@ensureDeferred
|
||||
async def test_generate(reactor, request, alice):
|
||||
async def skiptest_generate(reactor, request, alice):
|
||||
"""
|
||||
This is a helper for generating the test vectors.
|
||||
|
||||
You can re-generate the test vectors by fixing the name of the test and
|
||||
running it. Normally this test doesn't run because it ran once and we
|
||||
captured its output. Other tests run against that output and we want them
|
||||
to run against the results produced originally, not a possibly
|
||||
ever-changing set of outputs.
|
||||
"""
|
||||
results = await asyncfoldr(
|
||||
generate(reactor, request, alice),
|
||||
insert,
|
||||
@ -103,7 +154,21 @@ async def test_generate(reactor, request, alice):
|
||||
f.write(safe_dump(results))
|
||||
|
||||
|
||||
async def reconfigure(reactor, request, alice, params, convergence):
|
||||
async def reconfigure(reactor, request, alice: TahoeProcess, params: tuple[int, int], convergence: bytes) -> None:
|
||||
"""
|
||||
Reconfigure a Tahoe-LAFS node with different ZFEC parameters and
|
||||
convergence secret.
|
||||
|
||||
:param reactor: A reactor to use to restart the process.
|
||||
:param request: The pytest request object to use to arrange process
|
||||
cleanup.
|
||||
:param alice: The Tahoe-LAFS node to reconfigure.
|
||||
:param params: The ``needed`` and ``total`` ZFEC encoding parameters.
|
||||
:param convergence: The convergence secret.
|
||||
|
||||
:return: ``None`` after the node configuration has been rewritten, the
|
||||
node has been restarted, and the node is ready to provide service.
|
||||
"""
|
||||
needed, total = params
|
||||
config = read_config(alice.node_dir, "tub.port")
|
||||
config.set_config("client", "shares.happy", str(1))
|
||||
@ -119,26 +184,58 @@ async def reconfigure(reactor, request, alice, params, convergence):
|
||||
print("Ready.")
|
||||
|
||||
|
||||
async def generate(reactor, request, alice):
|
||||
async def generate(reactor, request, alice: TahoeProcess) -> AsyncGenerator[tuple[str, str], None]:
|
||||
"""
|
||||
Generate all of the test vectors using the given node.
|
||||
|
||||
:param reactor: The reactor to use to restart the Tahoe-LAFS node when it
|
||||
needs to be reconfigured.
|
||||
|
||||
:param request: The pytest request object to use to arrange process
|
||||
cleanup.
|
||||
|
||||
:param alice: The Tahoe-LAFS node to use to generate the test vectors.
|
||||
|
||||
:return: The yield values are two-tuples describing a test vector. The
|
||||
first element is a string describing a case and the second element is
|
||||
the CHK capability for that case.
|
||||
"""
|
||||
node_key = (None, None)
|
||||
for params, secret, data in product(ZFEC_PARAMS, CONVERGENCE_SECRETS, OBJECT_DATA):
|
||||
if node_key != (params, secret):
|
||||
await reconfigure(reactor, request, alice, params, secret)
|
||||
node_key = (params, secret)
|
||||
|
||||
yield key(params, secret, data), upload_immutable(alice, data)
|
||||
yield key(params, secret, data), upload(alice, "chk", data)
|
||||
|
||||
|
||||
def key(params, secret, data):
|
||||
def key(params: tuple[int, int], secret: bytes, data: bytes) -> str:
|
||||
"""
|
||||
Construct the key describing the case defined by the given parameters.
|
||||
|
||||
:param params: The ``needed`` and ``total`` ZFEC encoding parameters.
|
||||
:param secret: The convergence secret.
|
||||
:param data: The plaintext data.
|
||||
|
||||
:return: A distinct string for the given inputs, but shorter. This is
|
||||
suitable for use as, eg, a key in a dictionary.
|
||||
"""
|
||||
return f"{params[0]}/{params[1]},{digest(secret)},{digest(data)}"
|
||||
|
||||
|
||||
def upload_immutable(alice, data):
|
||||
def upload(alice: TahoeProcess, fmt: str, data: bytes) -> str:
|
||||
"""
|
||||
Upload the given data to the given node.
|
||||
|
||||
:param alice: The node to upload to.
|
||||
|
||||
:param fmt: The name of the format for the upload. CHK, SDMF, or MDMF.
|
||||
|
||||
:param data: The data to upload.
|
||||
|
||||
:return: The capability for the uploaded data.
|
||||
"""
|
||||
with NamedTemporaryFile() as f:
|
||||
f.write(data)
|
||||
f.flush()
|
||||
return cli(alice, "put", "--format=chk", f.name).decode("utf-8").strip()
|
||||
|
||||
|
||||
def digest(bs):
|
||||
return sha256(bs).hexdigest()
|
||||
return cli(alice, "put", f"--format={fmt}", f.name).decode("utf-8").strip()
|
||||
|
Loading…
x
Reference in New Issue
Block a user