diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py index a3364f5e4328b7a1b3e89958bfec34c66629b5b5..e917100e209e0cf9877fcf0e519623e2e1208ff3 100644 --- a/src/_zkapauthorizer/_storage_server.py +++ b/src/_zkapauthorizer/_storage_server.py @@ -230,7 +230,13 @@ class ZKAPAuthorizerStorageServer(Referenceable): same from the perspective of token validation. """ self._validate_tokens(tokens) - return self._original.remote_slot_testv_and_readv_and_writev(*a, **kw) + # Skip over the remotely exposed method and jump to the underlying + # implementation which accepts one additional parameter that we know + # about (and don't expose over the network): renew_leases. We always + # pass False for this because we want to manage leases completely + # separately from writes. + kw["renew_leases"] = False + return self._original.slot_testv_and_readv_and_writev(*a, **kw) def remote_slot_readv(self, *a, **kw): """ diff --git a/src/_zkapauthorizer/tests/test_storage_protocol.py b/src/_zkapauthorizer/tests/test_storage_protocol.py index d5fc174897d58d9fa97c777e1c6da4813aa8eab1..e98e64ef86096c39590f640bf84555538c093d55 100644 --- a/src/_zkapauthorizer/tests/test_storage_protocol.py +++ b/src/_zkapauthorizer/tests/test_storage_protocol.py @@ -437,6 +437,48 @@ class ShareTests(TestCase): sharenum, ), ) + @given( + storage_index=storage_indexes(), + secrets=tuples( + write_enabler_secrets(), + lease_renew_secrets(), + lease_cancel_secrets(), + ), + test_and_write_vectors_for_shares=test_and_write_vectors_for_shares(), + ) + def test_mutable_write_preserves_lease(self, storage_index, secrets, test_and_write_vectors_for_shares): + """ + When mutable share data is written using *slot_testv_and_readv_and_writev* + any leases on the corresponding slot remain the same. + """ + # Hypothesis causes our storage server to be used many times. Clean + # up between iterations. + cleanup_storage_server(self.anonymous_storage_server) + + wrote, read = extract_result( + self.client.slot_testv_and_readv_and_writev( + storage_index, + secrets=secrets, + tw_vectors={ + k: v.for_call() + for (k, v) + in test_and_write_vectors_for_shares.items() + }, + r_vector=[], + ), + ) + + self.assertThat( + wrote, + Equals(True), + u"Server rejected a write to a new mutable slot", + ) + + # There are *no* leases on this newly written slot! + self.assertThat( + list(self.anonymous_storage_server.get_slot_leases(storage_index)), + Equals([]), + ) def write_vector_to_read_vector(write_vector):