/// Integrity checks and diagnostic reports.
pub(all) struct VerificationReport {
result : VerifyResult
checked_chunks : Int
checked_bytes : Int
warnings : Array[String]
} derive(Show)
pub fn VerificationReport::result(self : VerificationReport) -> VerifyResult { self.result }
pub fn VerificationReport::passed(self : VerificationReport) -> Bool { self.result.ok() }
pub fn VerificationReport::checked(self : VerificationReport) -> Int { self.checked_chunks }
pub fn VerificationReport::bytes(self : VerificationReport) -> Int { self.checked_bytes }
pub fn VerificationReport::warnings(self : VerificationReport) -> Array[String] { self.warnings.copy() }
pub fn verify_store(store : ChunkStore, manifest : Manifest) -> VerificationReport {
if !manifest.is_valid() || !manifest_validate_order(manifest) {
return { result: InvalidManifest(reason="manifest metadata or root is invalid"), checked_chunks: 0, checked_bytes: 0, warnings: [] }
}
let mut checked = 0
let mut bytes = 0
let warnings = []
for reference in manifest.references() {
match store.get(reference.id()) {
None => return { result: MissingChunk(digest=reference.id(), position=reference.at()), checked_chunks: checked, checked_bytes: bytes, warnings }
Some(data) => {
let actual = digest_bytes(data)
if data.length() != reference.length() {
return { result: SizeMismatch(expected=reference.length(), actual=data.length()), checked_chunks: checked, checked_bytes: bytes, warnings }
}
if !digest_equal(actual, reference.id()) {
return { result: CorruptChunk(expected=reference.id(), actual=actual, position=reference.at()), checked_chunks: checked, checked_bytes: bytes, warnings }
}
checked = checked + 1
bytes = bytes + data.length()
}
}
}
if bytes != manifest.size() {
return { result: SizeMismatch(expected=manifest.size(), actual=bytes), checked_chunks: checked, checked_bytes: bytes, warnings }
}
{ result: Valid, checked_chunks: checked, checked_bytes: bytes, warnings }
}
pub fn verify_bytes(data : Array[Byte], expected : String) -> Bool {
digest_equal(digest_bytes(data), expected)
}
pub fn verify_manifest_root(manifest : Manifest) -> Bool {
manifest.root_digest() == merkle_root(manifest_digests(manifest))
}
pub fn verify_proof_for_manifest(manifest : Manifest, position : Int) -> Bool {
if position < 0 || position >= manifest.count() { return false }
let digests = manifest_digests(manifest)
let proof = merkle_proof(digests, position)
verify_merkle_proof(digests[position], proof, manifest.root_digest())
}
pub fn report_message(report : VerificationReport) -> String {
match report.result() {
Valid => "valid: \{report.checked()} chunks, \{report.bytes()} bytes"
InvalidManifest(reason~) => "invalid manifest: \{reason}"
MissingChunk(digest~, position~) => "missing chunk \{position}: \{digest}"
CorruptChunk(expected~, actual~, position~) => "corrupt chunk \{position}: expected \{expected}, got \{actual}"
SizeMismatch(expected~, actual~) => "size mismatch: expected \{expected}, got \{actual}"
}
}
pub fn collect_missing(store : ChunkStore, manifest : Manifest) -> Array[ChunkRef] {
let missing = []
for reference in manifest.references() {
if !store.has(reference.id()) { missing.push(reference) }
}
missing
}
pub fn collect_extra(store : ChunkStore, manifest : Manifest) -> Array[String] {
let extra = []
for digest in store.all_digests() {
if !manifest_contains(manifest, digest) { extra.push(digest) }
}
extra
}