Record Validator
Record validators are a generic mechanism for checking the DHT records, including:
Enforcing a data schema (e.g., checking content types)
Enforcing security requirements (e.g., allowing only the owner to update the record)
Enforcement using predicates/callables for customizable logic on the DHTRecord (e.g., checking keys match allowable keys, checking expiration dates, etc.)
When starting the DHT, it can be initialized with an Iterable of the RecordValidatorBase.
RecordValidatorBase
The RecordValidatorBase is the abstract base class for all record validators to implement.
@dataclasses.dataclass(init=True, repr=True, frozen=True)
class DHTRecord:
key: bytes
subkey: bytes
value: bytes
expiration_time: float
class DHTRecordRequestType(Enum):
GET = "get"
POST = "post"
class RecordValidatorBase(ABC):
"""
Record validators are a generic mechanism for checking the DHT records including:
- Enforcing a data schema (e.g. checking content types)
- Enforcing security requirements (e.g. allowing only the owner to update the record)
"""
@abstractmethod
def validate(self, record: DHTRecord, type: DHTRecordRequestType) -> bool:
"""
Should return whether the `record` is valid based on request type.
The valid records should have been extended with sign_value().
validate() is called when another DHT peer:
- Asks us to store the record
- Returns the record by our request
"""
pass
def sign_value(self, record: DHTRecord) -> bytes:
"""
Should return `record.value` extended with the record's signature.
Note: there's no need to overwrite this method if a validator doesn't use a signature.
sign_value() is called after the application asks the DHT to store the record.
"""
return record.value
def strip_value(self, record: DHTRecord) -> bytes:
"""
Should return `record.value` stripped of the record's signature.
strip_value() is only called if validate() was successful.
Note: there's no need to overwrite this method if a validator doesn't use a signature.
strip_value() is called before the DHT returns the record by the application's request.
"""
return record.value
@property
def priority(self) -> int:
"""
Defines the order of applying this validator with respect to other validators.
The validators are applied:
- In order of increasing priority for signing a record
- In order of decreasing priority for validating and stripping a record
"""
return 0
def merge_with(self, other: "RecordValidatorBase") -> bool:
"""
By default, all validators are applied sequentially (i.e. we require all validate() calls
to return True for a record to be validated successfully).
However, you may want to define another policy for combining your validator classes
(e.g. for schema validators, we want to require only one validate() call to return True
because each validator bears a part of the schema).
This can be achieved with overriding merge_with(). It should:
- Return True if it has successfully merged the `other` validator to `self`,
so that `self` became a validator that combines the old `self` and `other` using
the necessary policy. In this case, `other` should remain unchanged.
- Return False if the merging has not happened. In this case, both `self` and `other`
should remain unchanged. The DHT will try merging `other` to another validator or
add it as a separate validator (to be applied sequentially).
"""
return False
By default, the template comes with two main Record Validators:
Last updated