class Ordered(MessageBase): typename = ORDERED schema = ((f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.VALID_REQ_IDR.nm, IterableField( LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT))), (f.INVALID_REQ_IDR.nm, IterableField( LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT))), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.LEDGER_ID.nm, LedgerIdField()), (f.STATE_ROOT.nm, MerkleRootField(nullable=True)), (f.TXN_ROOT.nm, MerkleRootField(nullable=True)), (f.AUDIT_TXN_ROOT_HASH.nm, MerkleRootField(nullable=True)), (f.PRIMARIES.nm, IterableField( LimitedLengthStringField(max_length=NAME_FIELD_LIMIT))), (f.ORIGINAL_VIEW_NO.nm, NonNegativeNumberField()), (f.PLUGIN_FIELDS.nm, AnyMapField(optional=True, nullable=True)))
class BatchCommitted(MessageBase): """ Purpose: pass to Observable after each batch is committed (so that Observable can propagate the data to Observers using ObservedData msg) """ typename = BATCH_COMMITTED schema = ( (f.REQUESTS.nm, IterableField( ClientMessageValidator( operation_schema_is_strict=OPERATION_SCHEMA_IS_STRICT))), (f.LEDGER_ID.nm, LedgerIdField()), (f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.STATE_ROOT.nm, MerkleRootField()), (f.TXN_ROOT.nm, MerkleRootField()), (f.SEQ_NO_START.nm, NonNegativeNumberField()), (f.SEQ_NO_END.nm, NonNegativeNumberField()), (f.AUDIT_TXN_ROOT_HASH.nm, MerkleRootField(nullable=True)), (f.PRIMARIES.nm, IterableField(LimitedLengthStringField(max_length=NAME_FIELD_LIMIT))), (f.ORIGINAL_VIEW_NO.nm, NonNegativeNumberField()), )
class NewView(MessageBase): typename = NEW_VIEW schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.VIEW_CHANGES.nm, IterableField(ViewChangeField())), # list of tuples (node_name, view_change_digest) (f.CHECKPOINT.nm, AnyField()), # Checkpoint to be selected as stable (TODO: or tuple?) (f.BATCHES.nm, IterableField(BatchIDField())) # list of tuples (view_no, pp_view_no, pp_seq_no, pp_digest) # that should get into new view ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if isinstance(self.checkpoint, dict): self.checkpoint = Checkpoint(**self.checkpoint) # The field `batches` can to be a list of BatchIDs or of dicts. # If it's not a list of dicts then we don't need to deserialize it. if not self.batches or not isinstance(self.batches[0], dict): return self.batches = [BatchID(**bid) for bid in self.batches if isinstance(bid, dict)] def _asdict(self): result = super()._asdict() chk = self.checkpoint if not isinstance(chk, dict): result[f.CHECKPOINT.nm] = chk._asdict() # The field `batches` can to be a list of BatchIDs or of dicts. # If its a list of dicts then we don't need to serialize it. if not self.batches or not isinstance(self.batches[0], BatchID): return result result[f.BATCHES.nm] = [bid._asdict() for bid in self.batches] return result
class RevocRegEntryValueField(MessageValidator): schema = ( (PREV_ACCUM, NonEmptyStringField(optional=True)), (ACCUM, NonEmptyStringField()), (ISSUED, IterableField(inner_field_type=IntegerField(), optional=True)), (REVOKED, IterableField(inner_field_type=IntegerField(), optional=True)) )
class ViewChange(MessageBase): typename = VIEW_CHANGE schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.STABLE_CHECKPOINT.nm, NonNegativeNumberField()), (f.PREPARED.nm, IterableField(BatchIDField()) ), # list of tuples (view_no, pp_view_no, pp_seq_no, pp_digest) (f.PREPREPARED.nm, IterableField(BatchIDField()) ), # list of tuples (view_no, pp_view_no, pp_seq_no, pp_digest) (f.CHECKPOINTS.nm, IterableField(AnyField()) ) # list of Checkpoints TODO: should we change to tuples? ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) checkpoints = [] for chk in self.checkpoints: if isinstance(chk, dict): checkpoints.append(Checkpoint(**chk)) if checkpoints: self.checkpoints = checkpoints # The field `prepared` can to be a list of BatchIDs or of dicts. # If its a list of dicts then we need to deserialize it. if self.prepared and isinstance(self.prepared[0], dict): self.prepared = [ BatchID(**bid) for bid in self.prepared if isinstance(bid, dict) ] # The field `preprepared` can to be a list of BatchIDs or of dicts. # If its a list of dicts then we need to deserialize it. if self.preprepared and isinstance(self.preprepared[0], dict): self.preprepared = [ BatchID(**bid) for bid in self.preprepared if isinstance(bid, dict) ] def _asdict(self): result = super()._asdict() checkpoints = [] for chk in self.checkpoints: if isinstance(chk, dict): continue checkpoints.append(chk._asdict()) if checkpoints: result[f.CHECKPOINTS.nm] = checkpoints # The field `prepared` can to be a list of BatchIDs or of dicts. # If its a list of BatchID then we need to serialize it. if self.prepared and isinstance(self.prepared[0], BatchID): result[f.PREPARED.nm] = [bid._asdict() for bid in self.prepared] # The field `preprepared` can to be a list of BatchIDs or of dicts. # If its a list of BatchID then we need to serialize it. if self.preprepared and isinstance(self.preprepared[0], BatchID): result[f.PREPREPARED.nm] = [ bid._asdict() for bid in self.preprepared ] return result
class ViewChange(MessageBase): typename = VIEW_CHANGE schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.STABLE_CHECKPOINT.nm, NonNegativeNumberField()), (f.PREPARED.nm, IterableField(AnyField())), # list of PrePrepare (f.PREPREPARED.nm, IterableField(AnyField())), # list of PrePrepare (f.CHECKPOINTS.nm, IterableField(AnyField())) # list of Checkpoint )
class NewView(MessageBase): typename = NEW_VIEW schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.VIEW_CHANGES.nm, IterableField(ViewChangeField())), # list of tuples (node_name, view_change_digest) (f.CHECKPOINT.nm, AnyField()), # Checkpoint to be selected as stable (TODO: or tuple?) (f.BATCHES.nm, IterableField(BatchIDField())) # list of tuples (view_no, pp_seq_no, pp_digest) # that should get into new view )
class ViewChange(MessageBase): typename = VIEW_CHANGE schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.STABLE_CHECKPOINT.nm, NonNegativeNumberField()), (f.PREPARED.nm, IterableField(BatchIDField())), # list of tuples (view_no, pp_seq_no, pp_digest) (f.PREPREPARED.nm, IterableField(BatchIDField())), # list of tuples (view_no, pp_seq_no, pp_digest) (f.CHECKPOINTS.nm, IterableField(AnyField())) # list of Checkpoints TODO: should we change to tuples? )
class NewView(MessageBase): typename = NEW_VIEW schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.VIEW_CHANGES.nm, IterableField( AnyField())), # list of tuples (node_name, view_change_digest) (f.CHECKPOINT.nm, AnyField()), # Checkpoint to be selected as stable (f.PREPREPARES.nm, IterableField(AnyField()) ) # list of PrePrepares that should get into new view )
class PrePrepare(MessageBase): schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.REQ_IDR.nm, IterableField( LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT))), (f.DISCARDED.nm, SerializedValueField(nullable=True)), (f.DIGEST.nm, LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT)), (f.LEDGER_ID.nm, LedgerIdField()), (f.STATE_ROOT.nm, MerkleRootField(nullable=True)), (f.TXN_ROOT.nm, MerkleRootField(nullable=True)), (f.SUB_SEQ_NO.nm, NonNegativeNumberField()), (f.FINAL.nm, BooleanField()), (f.POOL_STATE_ROOT_HASH.nm, MerkleRootField(optional=True, nullable=True)), (f.AUDIT_TXN_ROOT_HASH.nm, MerkleRootField(optional=True, nullable=True)), # TODO: support multiple multi-sigs for multiple previous batches (f.BLS_MULTI_SIG.nm, BlsMultiSignatureField(optional=True, nullable=True)), (f.BLS_MULTI_SIGS.nm, IterableField(optional=True, inner_field_type=BlsMultiSignatureField( optional=True, nullable=True))), (f.ORIGINAL_VIEW_NO.nm, NonNegativeNumberField(optional=True, nullable=True)), (f.PLUGIN_FIELDS.nm, AnyMapField(optional=True, nullable=True)), ) typename = PREPREPARE def _post_process(self, input_as_dict: Dict) -> Dict: # make validated input hashable input_as_dict[f.REQ_IDR.nm] = tuple(input_as_dict[f.REQ_IDR.nm]) bls = input_as_dict.get(f.BLS_MULTI_SIG.nm, None) if bls is not None: input_as_dict[f.BLS_MULTI_SIG.nm] = (bls[0], tuple(bls[1]), tuple(bls[2])) bls_sigs = input_as_dict.get(f.BLS_MULTI_SIGS.nm, None) if bls_sigs is not None: sub = [] for sig in bls_sigs: sub.append((sig[0], tuple(sig[1]), tuple(sig[2]))) input_as_dict[f.BLS_MULTI_SIGS.nm] = tuple(sub) return input_as_dict
class Batch(MessageBase): typename = BATCH schema = ( (f.MSGS.nm, IterableField(SerializedValueField())), (f.SIG.nm, SignatureField(max_length=SIGNATURE_FIELD_LIMIT)), )
class BackupInstanceFaulty(MessageBase): typename = BACKUP_INSTANCE_FAULTY schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.INSTANCES.nm, IterableField(NonNegativeNumberField())), (f.REASON.nm, NonNegativeNumberField()) )
class PrePrepare(MessageBase): schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.REQ_IDR.nm, IterableField(LimitedLengthStringField( max_length=DIGEST_FIELD_LIMIT))), (f.DISCARDED.nm, SerializedValueField(nullable=True)), (f.DIGEST.nm, LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT)), (f.LEDGER_ID.nm, LedgerIdField()), (f.STATE_ROOT.nm, MerkleRootField(nullable=True)), (f.TXN_ROOT.nm, MerkleRootField(nullable=True)), (f.SUB_SEQ_NO.nm, NonNegativeNumberField()), (f.FINAL.nm, BooleanField()), (f.POOL_STATE_ROOT_HASH.nm, MerkleRootField(optional=True, nullable=True)), (f.AUDIT_TXN_ROOT_HASH.nm, MerkleRootField(optional=True, nullable=True)), # TODO: support multiple multi-sigs for multiple previous batches (f.BLS_MULTI_SIG.nm, BlsMultiSignatureField(optional=True, nullable=True)), (f.PLUGIN_FIELDS.nm, AnyMapField(optional=True, nullable=True)), ) typename = PREPREPARE
class ThreePCState(MessageBase): typename = THREE_PC_STATE schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.MSGS.nm, IterableField(ClientMessageValidator( operation_schema_is_strict=True))), )
class TxnFeesField(FixedLengthField): _base_types = (list, tuple) inputs_validator = PublicInputsField() outputs_validator = PublicOutputsField() signatures_validator = IterableField( SignatureField(max_length=SIGNATURE_FIELD_LIMIT)) def __init__(self, **kwargs): super().__init__(length=3, **kwargs) def _specific_validation(self, val): error = super()._specific_validation(val) if error: return error error = self.inputs_validator.validate(val[0]) if error: return error error = self.outputs_validator.validate(val[1]) if error: return error error = self.signatures_validator.validate(val[2]) if error: return error if len(val[0]) != len(val[2]): return 'Number of signatures and number of inputs should match but are {} and {} ' \ 'respectively.'.format(len(val[2]), len(val[0]))
class SchemaField(MessageValidator): schema = ( (SCHEMA_NAME, LimitedLengthStringField(max_length=NAME_FIELD_LIMIT)), (SCHEMA_VERSION, VersionField(components_number=(2, 3,), max_length=VERSION_FIELD_LIMIT)), (SCHEMA_ATTR_NAMES, IterableField( LimitedLengthStringField(max_length=NAME_FIELD_LIMIT), min_length=1)), )
def test_min_max_checks(): validator = IterableField(NonNegativeNumberField(), min_length=3, max_length=10) assert validator.validate(list(range(2))) for i in range(3, 11): assert not validator.validate(list(range(i))) assert validator.validate(list(range(12)))
class Reelection(MessageBase): typename = REELECTION schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.ROUND.nm, NonNegativeNumberField()), (f.TIE_AMONG.nm, IterableField(TieAmongField(max_length=TIE_IDR_FIELD_LIMIT))), (f.VIEW_NO.nm, NonNegativeNumberField()), )
class SetContextField(MessageValidator): context = ( (CONTEXT_NAME, LimitedLengthStringField(max_length=NAME_FIELD_LIMIT)), (CONTEXT_VERSION, VersionField(version_cls=ContextVersion)), (CONTEXT_CONTEXT_ARRAY, IterableField( LimitedLengthStringField(max_length=NAME_FIELD_LIMIT), min_length=1, max_length=CONTEXT_ATTRIBUTES_LIMIT)), )
class SchemaField(MessageValidator): schema = ( (NAME, NonEmptyStringField()), (VERSION, VersionField(components_number=( 2, 3, ))), (ATTR_NAMES, IterableField(NonEmptyStringField())), )
class SchemaField(MessageValidator): schema = ( (SCHEMA_NAME, LimitedLengthStringField(max_length=NAME_FIELD_LIMIT)), (SCHEMA_VERSION, VersionField(version_cls=SchemaVersion)), (SCHEMA_ATTR_NAMES, IterableField( LimitedLengthStringField(max_length=NAME_FIELD_LIMIT), min_length=1, max_length=SCHEMA_ATTRIBUTES_LIMIT)), )
class CatchupRep(MessageBase): typename = CATCHUP_REP schema = ( (f.LEDGER_ID.nm, LedgerIdField()), # TODO: turn on validation, the cause is INDY-388 # (f.TXNS.nm, MapField(key_field=StringifiedNonNegativeNumberField(), # value_field=ClientMessageValidator(operation_schema_is_strict=False))), (f.TXNS.nm, AnyValueField()), (f.CONS_PROOF.nm, IterableField(Base58Field(byte_lengths=(32, )))), )
class Ordered(MessageBase): typename = ORDERED schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.REQ_IDR.nm, IterableField(RequestIdentifierField())), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.LEDGER_ID.nm, LedgerIdField()), (f.STATE_ROOT.nm, MerkleRootField(nullable=True)), (f.TXN_ROOT.nm, MerkleRootField(nullable=True)), )
class ConsistencyProof(MessageBase): typename = CONSISTENCY_PROOF schema = ( (f.LEDGER_ID.nm, LedgerIdField()), (f.SEQ_NO_START.nm, NonNegativeNumberField()), (f.SEQ_NO_END.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.OLD_MERKLE_ROOT.nm, MerkleRootField()), (f.NEW_MERKLE_ROOT.nm, MerkleRootField()), (f.HASHES.nm, IterableField(LimitedLengthStringField(max_length=HASH_FIELD_LIMIT))), )
class CurrentState(MessageBase): """ Node sends this kind of message for nodes which suddenly reconnected (lagged). It contains information about current pool state, like view no, primary etc. """ typename = CURRENT_STATE schema = ( (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PRIMARY.nm, IterableField(AnyField())), # ViewChangeDone )
class ConstraintListField(MessageValidator): schema = ((CONSTRAINT_ID, ChooseField(values=ConstraintsEnum.values())), (AUTH_CONSTRAINTS, IterableField(AnyField()))) def _validate_message(self, val): constraints = val.get(AUTH_CONSTRAINTS) if not constraints: self._raise_invalid_message("Fields {} should not be an empty " "list.".format(AUTH_CONSTRAINTS)) for constraint in constraints: error_msg = ConstraintField(ConstraintEntityField(), self).validate(constraint) if error_msg: self._raise_invalid_message(error_msg)
class PrePrepare(MessageBase): typename = PREPREPARE schema = ( (f.INST_ID.nm, NonNegativeNumberField()), (f.VIEW_NO.nm, NonNegativeNumberField()), (f.PP_SEQ_NO.nm, NonNegativeNumberField()), (f.PP_TIME.nm, TimestampField()), (f.REQ_IDR.nm, IterableField(RequestIdentifierField())), (f.DISCARDED.nm, NonNegativeNumberField()), (f.DIGEST.nm, LimitedLengthStringField(max_length=DIGEST_FIELD_LIMIT)), (f.LEDGER_ID.nm, LedgerIdField()), (f.STATE_ROOT.nm, MerkleRootField(nullable=True)), (f.TXN_ROOT.nm, MerkleRootField(nullable=True)), )
def _transform_field(self, field, old_field_type, new_field_type): if isinstance(field, old_field_type): return new_field_type() elif self.__is_iterable_and_contains_type(field, old_field_type): return IterableField(new_field_type()) elif isinstance(field, MapField): key = field.key_field val = field.value_field if isinstance(field.key_field, old_field_type): key = new_field_type() if isinstance(field.value_field, old_field_type): val = new_field_type() return MapField(key, val) return field
class ViewChangeDone(MessageBase): """ Node sends this kind of message when view change steps done and it is ready to switch to the new primary. In contrast to 'Primary' message this one does not imply election. """ typename = VIEW_CHANGE_DONE schema = ( # name is nullable because this message can be sent when # there were no view changes and instance has no primary yet (f.VIEW_NO.nm, NonNegativeNumberField()), (f.NAME.nm, LimitedLengthStringField(max_length=NAME_FIELD_LIMIT, nullable=True)), (f.LEDGER_INFO.nm, IterableField(LedgerInfoField())))
class BatchCommitted(MessageBase): """ Purpose: pass to Observable after each batch is committed (so that Observable can propagate the data to Observers using ObservedData msg) """ typename = BATCH_COMMITTED schema = ((f.REQUESTS.nm, IterableField( ClientMessageValidator(operation_schema_is_strict=True))), (f.LEDGER_ID.nm, LedgerIdField()), (f.PP_TIME.nm, TimestampField()), (f.STATE_ROOT.nm, MerkleRootField()), (f.TXN_ROOT.nm, MerkleRootField()), (f.SEQ_NO_START.nm, NonNegativeNumberField()), (f.SEQ_NO_END.nm, NonNegativeNumberField()))