From f4aa41808694f4fd5b640d8c03389f367d523678 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Mon, 5 Oct 2009 15:18:49 -0700
Subject: [PATCH] Verifier: check the full cryptext-hash tree on each share.
 Removed .todos from the last few test_repairer tests that were waiting on
 this.

---
 src/allmydata/immutable/checker.py  |  5 +++++
 src/allmydata/immutable/download.py | 27 +++++++++++++++++++++++++++
 src/allmydata/test/test_repairer.py |  3 ---
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/src/allmydata/immutable/checker.py b/src/allmydata/immutable/checker.py
index cbdeb702..6e4b89e0 100644
--- a/src/allmydata/immutable/checker.py
+++ b/src/allmydata/immutable/checker.py
@@ -159,6 +159,11 @@ class Checker(log.PrefixingLogMixin):
             # block fetches won't send redundant share-hash-tree requests, to
             # speed things up. Then we fetch+validate all the blockhashes.
             d.addCallback(lambda ign: vrbp.get_all_blockhashes())
+
+            cht = IncompleteHashTree(vup.num_segments)
+            cht.set_hashes({0: vup.crypttext_root_hash})
+            d.addCallback(lambda ign: vrbp.get_all_crypttext_hashes(cht))
+
             d.addCallback(lambda ign: vrbp)
             return d
         d.addCallback(_got_ueb)
diff --git a/src/allmydata/immutable/download.py b/src/allmydata/immutable/download.py
index e86bf5c4..3dde40f1 100644
--- a/src/allmydata/immutable/download.py
+++ b/src/allmydata/immutable/download.py
@@ -419,6 +419,33 @@ class ValidatedReadBucketProxy(log.PrefixingLogMixin):
         d.addCallback(_got_block_hashes)
         return d
 
+    def get_all_crypttext_hashes(self, crypttext_hash_tree):
+        """Retrieve and validate all the crypttext-hash-tree nodes that are
+        in this share. Normally we don't look at these at all: the download
+        process fetches them incrementally as needed to validate each segment
+        of ciphertext. But this is a convenient place to give the Verifier a
+        function to validate all of these at once.
+
+        Call this with a new hashtree object for each share, initialized with
+        the crypttext hash tree root. I return a Deferred which errbacks upon
+        failure, probably with BadOrMissingHash.
+        """
+
+        # get_crypttext_hashes() always returns everything
+        d = self.bucket.get_crypttext_hashes()
+        def _got_crypttext_hashes(hashes):
+            if len(hashes) < len(crypttext_hash_tree):
+                raise BadOrMissingHash()
+            ct_hashes = dict(enumerate(hashes))
+            try:
+                crypttext_hash_tree.set_hashes(ct_hashes)
+            except IndexError, le:
+                raise BadOrMissingHash(le)
+            except (hashtree.BadHashError, hashtree.NotEnoughHashesError), le:
+                raise BadOrMissingHash(le)
+        d.addCallback(_got_crypttext_hashes)
+        return d
+
     def get_block(self, blocknum):
         # the first time we use this bucket, we need to fetch enough elements
         # of the share hash tree to validate it from our share hash up to the
diff --git a/src/allmydata/test/test_repairer.py b/src/allmydata/test/test_repairer.py
index 248d93e1..59886b25 100644
--- a/src/allmydata/test/test_repairer.py
+++ b/src/allmydata/test/test_repairer.py
@@ -252,7 +252,6 @@ class Verifier(GridTestMixin, unittest.TestCase, RepairTestMixin):
         self.basedir = "repairer/Verify/truncate_crypttext_hashtree"
         return self._help_test_verify(common._corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes,
                                       self.judge_invisible_corruption)
-    test_truncate_crypttext_hashtree.todo = "Verifier doesn't yet properly detect this kind of corruption."
 
     def test_corrupt_block_hashtree_offset(self):
         self.basedir = "repairer/Verify/corrupt_block_hashtree_offset"
@@ -273,13 +272,11 @@ class Verifier(GridTestMixin, unittest.TestCase, RepairTestMixin):
         self.basedir = "repairer/Verify/corrupt_crypttext_hashtree_offset"
         return self._help_test_verify(common._corrupt_offset_of_ciphertext_hash_tree,
                                       self.judge_invisible_corruption)
-    test_corrupt_crypttext_hashtree_offset.todo = "Verifier doesn't yet properly detect this kind of corruption."
 
     def test_corrupt_crypttext_hashtree(self):
         self.basedir = "repairer/Verify/corrupt_crypttext_hashtree"
         return self._help_test_verify(common._corrupt_crypttext_hash_tree,
                                       self.judge_invisible_corruption)
-    test_corrupt_crypttext_hashtree.todo = "Verifier doesn't yet properly detect this kind of corruption."
 
     def test_corrupt_block_hashtree(self):
         self.basedir = "repairer/Verify/corrupt_block_hashtree"
-- 
2.45.2