diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py index 1f96295374e30064d0c51e9ade02bfab3cc135df..95a45a4388485c136e00087d29db949046e84271 100644 --- a/src/_zkapauthorizer/_plugin.py +++ b/src/_zkapauthorizer/_plugin.py @@ -140,8 +140,7 @@ class ZKAPAuthorizer(object): registry=registry, ) storage_server = ZKAPAuthorizerStorageServer( - # unwrap the Foolscap layer, we'll do it ourselves. - anonymous_storage_server._server, + anonymous_storage_server, pass_value=pass_value, signing_key=signing_key, spender=spender, diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py index 8ff606a549a15f07828a8af4d70c2142b864826e..0a4b76db61fcc0d89a66ff52a09faab8a7d978cc 100644 --- a/src/_zkapauthorizer/_storage_server.py +++ b/src/_zkapauthorizer/_storage_server.py @@ -232,6 +232,10 @@ class ZKAPAuthorizerStorageServer(Referenceable): notification when a bucket writer is closed. It removes the disconnection-based cleanup callback for the given bucket. """ + # This implementation was originally copied from + # allmydata.storage.server.FoolscapStorageServer. Since we don't use + # Tahoe's Foolscap storage server layer we need to do this bucket + # writer bookkeeping ourselves. if bw in self._bucket_writer_disconnect_markers: canary, disconnect_marker = self._bucket_writer_disconnect_markers.pop(bw) canary.dontNotifyOnDisconnect(disconnect_marker) diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py index fb67ec74de980344e3d0a45f7c3db52b06b8d637..726be23690610c5681067e6f857f0c2a345bf30b 100644 --- a/src/_zkapauthorizer/model.py +++ b/src/_zkapauthorizer/model.py @@ -96,10 +96,9 @@ def open_and_initialize(path, connect=None): except OSError as e: raise StoreOpenError(e) - dbfile = path.asBytesMode().path try: conn = connect( - dbfile, + path.path, isolation_level="IMMEDIATE", ) except OperationalError as e: diff --git a/src/_zkapauthorizer/spending.py b/src/_zkapauthorizer/spending.py index e3af5af91919bd9c075343b6f418a67bbb6970c5..f718ac0056d609af45a9f0c5488ca6abb93e3571 100644 --- a/src/_zkapauthorizer/spending.py +++ b/src/_zkapauthorizer/spending.py @@ -16,6 +16,8 @@ A module for logic controlling the manner in which ZKAPs are spent. """ +from __future__ import annotations + from typing import Callable, List, Tuple import attr @@ -126,7 +128,7 @@ class PassGroup(object): def unblinded_tokens(self) -> List[UnblindedToken]: return list(unblinded_token for (unblinded_token, pass_) in self._tokens) - def split(self, select_indices: List[int]) -> ("PassGroup", "PassGroup"): + def split(self, select_indices: List[int]) -> (PassGroup, PassGroup): selected = [] unselected = [] for idx, t in enumerate(self._tokens): @@ -139,7 +141,7 @@ class PassGroup(object): attr.evolve(self, tokens=unselected), ) - def expand(self, by_amount: int) -> "PassGroup": + def expand(self, by_amount: int) -> PassGroup: return attr.evolve( self, tokens=self._tokens + self._factory.get(self._message, by_amount)._tokens, diff --git a/src/_zkapauthorizer/tests/fixtures.py b/src/_zkapauthorizer/tests/fixtures.py index a01dc6ff430eef143a73c0b0242767514b136dbb..8803180f62410c0f62e7032563ef6af7ae142a3c 100644 --- a/src/_zkapauthorizer/tests/fixtures.py +++ b/src/_zkapauthorizer/tests/fixtures.py @@ -36,7 +36,7 @@ class AnonymousStorageServer(Fixture): :ivar tempdir: The path to the server's storage on the filesystem. - :ivar backend: The protocol-agnostic storage server backend. + :ivar storage_server: The protocol-agnostic storage server backend. :ivar clock: The ``IReactorTime`` provider to supply to ``StorageServer`` for its time-checking needs. @@ -45,11 +45,11 @@ class AnonymousStorageServer(Fixture): clock: Clock = attr.ib() tempdir: FilePath = attr.ib(default=None) - backend: StorageServer = attr.ib(default=None) + storage_server: StorageServer = attr.ib(default=None) def _setUp(self): self.tempdir = FilePath(self.useFixture(TempDir()).join(u"storage")) - self.backend = StorageServer( + self.storage_server = StorageServer( self.tempdir.path, b"x" * 20, clock=self.clock, diff --git a/src/_zkapauthorizer/tests/foolscap.py b/src/_zkapauthorizer/tests/foolscap.py index 9512f47c234d0c7ade266b2a80fb6056b59c9524..b10b00ed48a1ccdc75d3c6037ebeaf0dd8fc66c1 100644 --- a/src/_zkapauthorizer/tests/foolscap.py +++ b/src/_zkapauthorizer/tests/foolscap.py @@ -17,7 +17,6 @@ Testing helpers related to Foolscap. """ import attr -from allmydata.interfaces import RIStorageServer from foolscap.api import Any, Copyable, Referenceable, RemoteInterface from foolscap.copyable import CopyableSlicer, ICopyable from twisted.internet.defer import fail, succeed @@ -33,19 +32,13 @@ class RIEcho(RemoteInterface): return Any() -class StubStorageBackend(object): +class StubStorageServer(object): def register_bucket_writer_close_handler(self, handler): pass -@implementer(RIStorageServer) -@attr.s -class StubFoolscapStorageServer(object): - _server = attr.ib(default=attr.Factory(StubStorageBackend)) - - def get_anonymous_storage_server(): - return StubFoolscapStorageServer() + return StubStorageServer() class BrokenCopyable(Copyable): diff --git a/src/_zkapauthorizer/tests/storage_common.py b/src/_zkapauthorizer/tests/storage_common.py index 6538b3bc4afee1cab06b5f9b57c601eb79dbefd8..39d85d25c6132ab0b8a14cddc267787b1e160619 100644 --- a/src/_zkapauthorizer/tests/storage_common.py +++ b/src/_zkapauthorizer/tests/storage_common.py @@ -36,13 +36,17 @@ from .strategies import bytes_for_share # Not really a strategy... LEASE_INTERVAL = 60 * 60 * 24 * 31 -def cleanup_storage_server(storage_server): +def reset_storage_server(storage_server): """ - Delete all of the shares held by the given storage server. + Restore a storage server to a default state. This includes + deleting all of the shares it holds. :param allmydata.storage.server.StorageServer storage_server: The storage server with some on-disk shares to delete. """ + # A storage server is read-write by default. + storage_server.readonly_storage = False + starts = [ FilePath(storage_server.sharedir), FilePath(storage_server.corruption_advisory_dir), diff --git a/src/_zkapauthorizer/tests/test_storage_protocol.py b/src/_zkapauthorizer/tests/test_storage_protocol.py index 2ee75aaae77a34dadf1c239c2397ceddcd852fc7..10e30c0fa51da66f27f975a44bc16dd6689cd65f 100644 --- a/src/_zkapauthorizer/tests/test_storage_protocol.py +++ b/src/_zkapauthorizer/tests/test_storage_protocol.py @@ -61,10 +61,10 @@ from .foolscap import LocalRemote from .matchers import matches_spent_passes, matches_version_dictionary from .storage_common import ( LEASE_INTERVAL, - cleanup_storage_server, get_passes, pass_factory, privacypass_passes, + reset_storage_server, whitebox_write_sparse_share, write_toy_shares, ) @@ -154,13 +154,13 @@ class ShareTests(TestCase): ) self.clock = Clock() - self.storage = self.useFixture( + self.anonymous_storage_server = self.useFixture( AnonymousStorageServer(self.clock), - ) + ).storage_server self.spending_recorder, spender = RecordingSpender.make() self.server = ZKAPAuthorizerStorageServer( - self.storage.backend, + self.anonymous_storage_server, self.pass_value, self.signing_key, spender, @@ -191,7 +191,7 @@ class ShareTests(TestCase): self.spending_recorder.reset() # And clean out any shares that might confuse things. - cleanup_storage_server(self.storage.backend) + reset_storage_server(self.anonymous_storage_server) def test_get_version(self): """ @@ -410,7 +410,7 @@ class ShareTests(TestCase): # Create some shares to alter the behavior of the next # allocate_buckets. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -479,7 +479,7 @@ class ShareTests(TestCase): ) self.assertThat( - dict(get_lease_grant_times(self.storage.backend, storage_index)), + dict(get_lease_grant_times(self.anonymous_storage_server, storage_index)), Equals(expected_leases), ) @@ -503,7 +503,7 @@ class ShareTests(TestCase): # Create a share we can toy with. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, add_lease_secret, cancel_secret, @@ -526,7 +526,7 @@ class ShareTests(TestCase): matches_spent_passes(self.public_key_hash, self.pass_factory.spent), ) - leases = list(self.storage.backend.get_leases(storage_index)) + leases = list(self.anonymous_storage_server.get_leases(storage_index)) self.assertThat(leases, HasLength(2)) def _stat_shares_immutable_test( @@ -539,7 +539,7 @@ class ShareTests(TestCase): # Create a share we can toy with. write_shares( - self.storage, + self.anonymous_storage_server, storage_index, {sharenum}, size, @@ -548,7 +548,7 @@ class ShareTests(TestCase): # Perhaps put some more leases on it. Leases might impact our # ability to determine share data size. for renew_secret in leases: - self.storage.backend.add_lease( + self.anonymous_storage_server.add_lease( storage_index, renew_secret, cancel_secret, @@ -589,8 +589,8 @@ class ShareTests(TestCase): size, when, leases, - lambda storage, storage_index, sharenums, size, canary: write_toy_shares( - storage.backend, + lambda storage_server, storage_index, sharenums, size, canary: write_toy_shares( + storage_server, storage_index, renew_secret, cancel_secret, @@ -616,7 +616,7 @@ class ShareTests(TestCase): """ assume(version not in (1, 2)) - sharedir = FilePath(self.storage.backend.sharedir).preauthChild( + sharedir = FilePath(self.anonymous_storage_server.sharedir).preauthChild( # storage_index_to_dir likes to return multiple segments # joined by pathsep storage_index_to_dir(storage_index), @@ -658,7 +658,7 @@ class ShareTests(TestCase): ``stat_shares`` declines to offer a result (by raising ``ValueError``). """ - sharedir = FilePath(self.storage.backend.sharedir).preauthChild( + sharedir = FilePath(self.anonymous_storage_server.sharedir).preauthChild( # storage_index_to_dir likes to return multiple segments # joined by pathsep storage_index_to_dir(storage_index), @@ -710,8 +710,8 @@ class ShareTests(TestCase): write real multi-gigabyte files to exercise the behavior. """ - def write_shares(storage, storage_index, sharenums, size, canary): - sharedir = FilePath(storage.backend.sharedir).preauthChild( + def write_shares(storage_server, storage_index, sharenums, size, canary): + sharedir = FilePath(storage_server.sharedir).preauthChild( # storage_index_to_dir likes to return multiple segments # joined by pathsep storage_index_to_dir(storage_index), @@ -816,7 +816,7 @@ class ShareTests(TestCase): """ # Create a share we can toy with. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -834,7 +834,7 @@ class ShareTests(TestCase): succeeded(Always()), ) self.assertThat( - FilePath(self.storage.backend.corruption_advisory_dir).children(), + FilePath(self.anonymous_storage_server.corruption_advisory_dir).children(), HasLength(1), ) @@ -924,7 +924,7 @@ class ShareTests(TestCase): self.assertThat( dict( get_lease_grant_times( - self.storage.backend, + self.anonymous_storage_server, storage_index, ) ), @@ -952,7 +952,9 @@ class ShareTests(TestCase): def leases(): return list( lease.to_mutable_data() - for lease in self.storage.backend.get_slot_leases(storage_index) + for lease in self.anonymous_storage_server.get_slot_leases( + storage_index + ) ) def write(): diff --git a/src/_zkapauthorizer/tests/test_storage_server.py b/src/_zkapauthorizer/tests/test_storage_server.py index defab2b6842a34ffc513cc9a444a61a6ea0dd890..a0237bb7b82cf5c6069d7ae6f243bb37479f22d1 100644 --- a/src/_zkapauthorizer/tests/test_storage_server.py +++ b/src/_zkapauthorizer/tests/test_storage_server.py @@ -45,7 +45,7 @@ from ..storage_common import ( from .common import skipIf from .fixtures import AnonymousStorageServer from .matchers import matches_spent_passes, raises -from .storage_common import cleanup_storage_server, get_passes, write_toy_shares +from .storage_common import get_passes, reset_storage_server, write_toy_shares from .strategies import ( lease_cancel_secrets, lease_renew_secrets, @@ -194,15 +194,15 @@ class PassValidationTests(TestCase): # the same time so we can do lease expiration calculations more # easily. self.clock.advance(time()) - self.storage = self.useFixture( + self.anonymous_storage_server = self.useFixture( AnonymousStorageServer(self.clock), - ) + ).storage_server self.signing_key = random_signing_key() self.public_key_hash = PublicKey.from_signing_key( self.signing_key ).encode_base64() self.storage_server = ZKAPAuthorizerStorageServer( - self.storage.backend, + self.anonymous_storage_server, self.pass_value, self.signing_key, spender, @@ -222,10 +222,7 @@ class PassValidationTests(TestCase): # Hypothesis and testtools fixtures don't play nicely together in a # way that allows us to just move everything from `setUp` into this # method. - cleanup_storage_server(self.storage.backend) - # One of the tests makes the server read-only partway through. Make - # sure that doesn't leak into other examples. - self.storage.backend.readonly_storage = False + reset_storage_server(self.anonymous_storage_server) self.spending_recorder.reset() @@ -532,7 +529,7 @@ class PassValidationTests(TestCase): ) # Create some shares at a slot which will require lease renewal. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -805,7 +802,7 @@ class PassValidationTests(TestCase): # the subsequent `allocate_buckets` operation - but of which the # client is unaware. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -875,7 +872,7 @@ class PassValidationTests(TestCase): ): # Create some shares at a slot which will require lease renewal. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -938,7 +935,7 @@ class PassValidationTests(TestCase): # Put some shares up there to target with the add_lease operation. write_toy_shares( - self.storage.backend, + self.anonymous_storage_server, storage_index, renew_secret, cancel_secret, @@ -957,7 +954,7 @@ class PassValidationTests(TestCase): # Turn off space-allocating operations entirely. Since there will be # no space for a new lease, the operation will fail. - self.storage.backend.readonly_storage = True + self.anonymous_storage_server.readonly_storage = True try: self.storage_server.doRemoteCall(