This commit is contained in:
2022-04-12 15:25:29 +02:00
parent 61b4270332
commit 03de86597f
15 changed files with 1164 additions and 501 deletions

View File

@@ -1,4 +1,4 @@
"""Format validator for raw (JSON) documents in the es.4 format"""
"""Document class for the es.4 format"""
from datetime import datetime, timezone
from hashlib import sha256
@@ -29,9 +29,7 @@ class RawDocument(TypedDict, total=False):
class Es4Document(Document): # pylint: disable=too-many-instance-attributes
"""Validator for the 'es.4' format
Checks if documents are spec-compliant before ingesting, and signs them according to spec.
"""An es.4 format document
See https://earthstar-project.org/specs/data-spec
"""
@@ -129,7 +127,7 @@ class Es4Document(Document): # pylint: disable=too-many-instance-attributes
signature: Optional[str] = None,
delete_after: Optional[datetime] = None,
):
self.author: Identity = author
self.author = author
self.path = path
self.signature = signature
self._content = content or ''
@@ -142,6 +140,8 @@ class Es4Document(Document): # pylint: disable=too-many-instance-attributes
def from_json(cls, raw_document: Dict[str, Any]) -> 'Es4Document':
"""Validate raw_document as an es.4 document and create an ``Es4Document`` from it
Checks if documents are spec-compliant before ingesting, and signs them according to spec.
:returns: a new ``Es4Document``
:raises ValidationError: if anything is wrong
"""
@@ -245,14 +245,24 @@ class Es4Document(Document): # pylint: disable=too-many-instance-attributes
hasher = sha256()
for key, value in sorted(hash_keys.items(), key=lambda elem: elem[0]):
# Skip null fields
if value is None:
continue
# Otherwise, append the fieldname and value.
# Tab and newline are our field separators.
# Convert integers to strings here.
# (The newline is included on the last field.)
hasher.update(f'{key}\t{value}\n'.encode('utf-8'))
# Binary digest, not hex digest string! Then convert bytes to Earthstar b32 format with
# leading 'b'.
return base32_bytes_to_string(hasher.digest())
def sign(self, identity: Optional[Identity] = None) -> None:
"""Sign the document and store the signature into the document (mutating it)
"""
if identity and identity != self.author:
raise ValidationError(
"when signing a document, keypair address must match document author"
@@ -358,3 +368,21 @@ class Es4Document(Document): # pylint: disable=too-many-instance-attributes
if content is not None and calculated_hash != content_hash:
raise ValidationError("content does not match contentHash")
@staticmethod
def compare_newest_first(doc_a, doc_b):
"""Compare two documents based on their time stamp"""
if doc_a.timestamp < doc_b.timestamp:
return Cmp.LT
if doc_b.timestamp > doc_b.timestamp:
return Cmp.GT
if doc_a.signature < doc_b.signature:
return Cmp.LT
if doc_a.signature > doc_b.signature:
return Cmp.GT
return Cmp.EQ