From c56b85c3d0f1e56dea8cd4e0ed6f7c5715cb8b77 Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Thu, 10 Oct 2019 16:15:00 -0400
Subject: [PATCH] [wip] get the client to supply the right number of passes

---
 src/_zkapauthorizer/_storage_client.py | 49 ++++++++++++++++++++------
 src/_zkapauthorizer/_storage_server.py | 12 +++++++
 src/_zkapauthorizer/foolscap.py        | 15 ++++++++
 3 files changed, 66 insertions(+), 10 deletions(-)

diff --git a/src/_zkapauthorizer/_storage_client.py b/src/_zkapauthorizer/_storage_client.py
index eafc119..8a9acdf 100644
--- a/src/_zkapauthorizer/_storage_client.py
+++ b/src/_zkapauthorizer/_storage_client.py
@@ -25,7 +25,10 @@ import attr
 from zope.interface import (
     implementer,
 )
-
+from twisted.internet.defer import (
+    inlineCallbacks,
+    returnValue,
+)
 from allmydata.interfaces import (
     IStorageServer,
 )
@@ -38,6 +41,7 @@ from .storage_common import (
     renew_lease_message,
     slot_testv_and_readv_and_writev_message,
     has_writes,
+    get_implied_data_length,
 )
 
 @implementer(IStorageServer)
@@ -164,6 +168,7 @@ class ZKAPAuthorizerStorageClient(object):
             reason,
         )
 
+    @inlineCallbacks
     def slot_testv_and_readv_and_writev(
             self,
             storage_index,
@@ -172,17 +177,41 @@ class ZKAPAuthorizerStorageClient(object):
             r_vector,
     ):
         if has_writes(tw_vectors):
-            passes = self._get_encoded_passes(slot_testv_and_readv_and_writev_message(storage_index), 1)
+            current_size = yield self._rref.callRemote(
+                "slot_share_sizes",
+                storage_index,
+                set(tw_vectors),
+            )
+            if current_size is None:
+                current_pass_count = 0
+            else:
+                current_pass_count = required_passes(BYTES_PER_PASS, {0}, current_size)
+            new_size = sum(
+                (
+                    get_implied_data_length(data_vector, length)
+                    for (_, data_vector, length)
+                    in tw_vectors.values()
+                ),
+                0,
+            )
+            new_pass_count = required_passes(BYTES_PER_PASS, {0}, new_size)
+            pass_count_increase = new_pass_count - current_pass_count
+            passes = self._get_encoded_passes(
+                slot_testv_and_readv_and_writev_message(storage_index),
+                pass_count_increase,
+            )
         else:
             passes = []
-        return self._rref.callRemote(
-            "slot_testv_and_readv_and_writev",
-            passes,
-            storage_index,
-            secrets,
-            tw_vectors,
-            r_vector,
-        )
+        returnValue((
+            yield self._rref.callRemote(
+                "slot_testv_and_readv_and_writev",
+                passes,
+                storage_index,
+                secrets,
+                tw_vectors,
+                r_vector,
+            )
+        ))
 
     def slot_readv(
             self,
diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py
index 061d613..d57fb9c 100644
--- a/src/_zkapauthorizer/_storage_server.py
+++ b/src/_zkapauthorizer/_storage_server.py
@@ -24,6 +24,10 @@ from __future__ import (
     absolute_import,
 )
 
+from errno import (
+    ENOENT,
+)
+
 from functools import (
     partial,
 )
@@ -234,6 +238,14 @@ class ZKAPAuthorizerStorageServer(Referenceable):
         """
         return self._original.remote_advise_corrupt_share(*a, **kw)
 
+    def remote_slot_share_sizes(self, storage_index, sharenums):
+        try:
+            return get_slot_share_size(self._original, storage_index, sharenums)
+        except OSError as e:
+            if e.errno == ENOENT:
+                return None
+            raise
+
     def remote_slot_testv_and_readv_and_writev(
             self,
             passes,
diff --git a/src/_zkapauthorizer/foolscap.py b/src/_zkapauthorizer/foolscap.py
index 948aff3..088dbcc 100644
--- a/src/_zkapauthorizer/foolscap.py
+++ b/src/_zkapauthorizer/foolscap.py
@@ -6,7 +6,9 @@ from foolscap.constraint import (
     ByteStringConstraint,
 )
 from foolscap.api import (
+    SetOf,
     ListOf,
+    ChoiceOf,
 )
 from foolscap.remoteinterface import (
     RemoteMethodSchema,
@@ -14,7 +16,10 @@ from foolscap.remoteinterface import (
 )
 
 from allmydata.interfaces import (
+    MAX_BUCKETS,
+    StorageIndex,
     RIStorageServer,
+    Offset,
 )
 
 # The Foolscap convention seems to be to try to constrain inputs to valid
@@ -110,6 +115,16 @@ class RIPrivacyPassAuthorizedStorageServer(RemoteInterface):
 
     get_buckets = RIStorageServer["get_buckets"]
 
+    def slot_share_sizes(
+            storage_index=StorageIndex,
+            sharenums=SetOf(int, maxLength=MAX_BUCKETS),
+    ):
+        """
+        Get the size of the given shares in the given storage index.  If there are
+        no shares, ``None``.
+        """
+        return ChoiceOf(None, Offset)
+
     slot_readv = RIStorageServer["slot_readv"]
 
     slot_testv_and_readv_and_writev = add_passes(
-- 
GitLab