Skip to main content
Validation is the final cryptographic verification step in TAPLE Core’s event lifecycle. Validators ensure event integrity and maintain the distributed ledger’s security. This guide covers validation mechanisms and proof verification.

Understanding Validation

Validation in TAPLE Core provides:
  • Cryptographic Verification: Events are verified by independent validators
  • Proof Generation: Validators create signed validation proofs
  • Quorum Requirements: Configurable threshold of validator signatures
  • Chain Integrity: Ensures continuity and consistency of event chains
The validation system is implemented in core/src/validation/validation.rs:21.

Validation Structure

pub struct Validation<C: DatabaseCollection> {
    gov_api: GovernanceAPI,
    database: DB<C>,
    signature_manager: SelfSignatureManager,
    message_channel: SenderEnd<MessageTaskCommand<TapleMessages>, ()>,
    derivator: DigestDerivator,
}

Validation Proof

A validation proof contains complete event metadata:
pub struct ValidationProof {
    pub subject_id: DigestIdentifier,
    pub schema_id: String,
    pub namespace: String,
    pub name: String,
    pub subject_public_key: KeyIdentifier,
    pub governance_id: DigestIdentifier,
    pub genesis_governance_version: u64,
    pub sn: u64,
    pub prev_event_hash: DigestIdentifier,
    pub event_hash: DigestIdentifier,
    pub governance_version: u64,
}

Validation Event

Validation requests include:
pub struct ValidationEvent {
    pub proof: ValidationProof,
    pub subject_signature: Signature,
    pub previous_proof: Option<ValidationProof>,
    pub prev_event_validation_signatures: HashSet<Signature>,
}

Validation Process

1
1. Receive Validation Request
2
Validators receive validation events from event owners:
3
let response = validation.validation_event(
    validation_event,
    sender
).await?;
4
Implemented in core/src/validation/validation.rs:46.
5
2. Check Governance Version
6
Verify governance version consistency:
7
let actual_gov_version = gov_api
    .get_governance_version(
        validation_event.proof.governance_id.clone(),
        validation_event.proof.subject_id.clone(),
    )
    .await?;

if actual_gov_version < validation_event.proof.governance_version {
    return Err(ValidationError::GovernanceVersionTooHigh);
} else if actual_gov_version > validation_event.proof.governance_version {
    // Notify sender of higher governance
    return Err(ValidationError::GovernanceVersionTooLow);
}
8
See core/src/validation/validation.rs:51-96.
9
3. Verify Subject Signature
10
Validate the subject’s signature on the proof:
11
if validation_event
    .subject_signature
    .verify(&validation_event.proof)
    .is_err()
{
    return Err(ValidationError::SubjectSignatureNotValid);
}
12
4. Check Previous Proofs
13
Verify proof chain continuity:
14
let subject_pk = self.check_proofs(
    &validation_event.proof,
    validation_event.previous_proof,
    validation_event.prev_event_validation_signatures,
    last_proof,
).await?;
15
Implemented in core/src/validation/validation.rs:117.
16
5. Store Validation Register
17
Save the validation proof:
18
self.database
    .set_validation_register(
        &validation_event.proof.subject_id,
        &validation_event.proof
    )
    .map_err(|_| ValidationError::DatabaseError)?;
19
6. Sign and Send Response
20
Generate validation signature and send response:
21
let validation_signature = self
    .signature_manager
    .sign(&validation_event.proof, self.derivator)
    .map_err(ValidationError::ProtocolErrors)?;

self.message_channel
    .tell(MessageTaskCommand::Request(
        None,
        TapleMessages::EventMessage(EventCommand::ValidatorResponse {
            event_hash: validation_event.proof.event_hash,
            signature: validation_signature.clone(),
            governance_version: actual_gov_version,
        }),
        vec![sender],
        MessageConfig::direct_response(),
    ))
    .await?;
22
See core/src/validation/validation.rs:132-147.

Proof Checking

Check Proof Continuity

The check_proofs method verifies:
async fn check_proofs(
    &self,
    new_proof: &ValidationProof,
    previous_proof: Option<ValidationProof>,
    validation_signatures: HashSet<Signature>,
    last_proof: Option<ValidationProof>,
) -> Result<KeyIdentifier, ValidationError>

Sequence Number Validation

Ensures events are sequential:
if last_proof.sn > new_proof.sn {
    Err(ValidationError::EventSnLowerThanLastSigned)
} else if last_proof.sn + 1 == new_proof.sn {
    // Verify previous proof
    self.validate_previous_proof(new_proof, last_proof, None).await
}
Implemented in core/src/validation/validation.rs:162-183.

Proof Similarity Check

Verifies proof consistency:
if !last_proof.is_similar(&new_proof) {
    Err(ValidationError::DifferentProofForEvent)
} else {
    Ok(last_proof.subject_public_key)
}

Previous Proof Validation

Validates the previous proof in the chain:
async fn validate_previous_proof(
    &self,
    new_proof: &ValidationProof,
    previous_proof: ValidationProof,
    validation_signatures: Option<HashSet<Signature>>,
) -> Result<KeyIdentifier, ValidationError>
Implemented in core/src/validation/validation.rs:229.

Proof Field Verification

Checks all critical fields match:
if previous_proof.event_hash != new_proof.prev_event_hash {
    return Err(ValidationError::DifferentProofForEvent);
}
if previous_proof.sn + 1 != new_proof.sn {
    return Err(ValidationError::DifferentProofForEvent);
}
if previous_proof.genesis_governance_version != new_proof.genesis_governance_version {
    return Err(ValidationError::DifferentProofForEvent);
}
if previous_proof.namespace != new_proof.namespace {
    return Err(ValidationError::DifferentProofForEvent);
}
if previous_proof.subject_id != new_proof.subject_id {
    return Err(ValidationError::DifferentProofForEvent);
}
See core/src/validation/validation.rs:237-260.

Signature Verification

Verify Previous Signatures

Validates signatures on previous proof:
let actual_signers: Result<HashSet<KeyIdentifier>, ValidationError> =
    validation_signatures
        .into_iter()
        .map(|signature| {
            if signature.verify(&previous_proof).is_err() {
                return Err(ValidationError::InvalidSignature);
            }
            Ok(signature.signer)
        })
        .collect();
Implemented in core/src/validation/validation.rs:263-272.

Check Signer Authorization

Verifies signers are authorized:
if !actual_signers.is_subset(&signers) {
    return Err(ValidationError::InvalidSigner);
}

Verify Quorum

Ensures quorum is reached:
if actual_signers.len() < quorum_size as usize {
    return Err(ValidationError::QuorumNotReached);
}

Getting Signers and Quorum

Retrieve validation requirements from governance:
async fn get_signers_and_quorum(
    &self,
    metadata: Metadata,
    stage: ValidationStage,
) -> Result<(HashSet<KeyIdentifier>, u32), ValidationError> {
    let signers = self
        .gov_api
        .get_signers(metadata.clone(), stage.clone())
        .await
        .map_err(ValidationError::GovernanceError)?;
    
    let quorum_size = self
        .gov_api
        .get_quorum(metadata, stage)
        .await
        .map_err(ValidationError::GovernanceError)?;
    
    Ok((signers, quorum_size))
}
Implemented in core/src/validation/validation.rs:292.

Database Operations

Store Validation Register

Persist validation proof:
db.set_validation_register(
    &subject_id,
    &validation_proof
)?;
Defined in core/src/database/db.rs:267.

Get Validation Register

Retrieve stored validation proof:
let proof = db.get_validation_register(&subject_id)?;
See core/src/database/db.rs:260.

Store Validation Proof with Signatures

Store complete validation data:
db.set_signatures(
    &subject_id,
    sn,
    signatures,
    validation_proof
)?;
Implemented in core/src/database/db.rs:107.

Get Validation Proof

Retrieve validation proof and signatures:
let (signatures, proof) = api.get_validation_proof(subject_id).await?;
Defined in core/src/api/api.rs:370.

Validation Stages

Validation occurs at the Validate stage:
pub enum ValidationStage {
    Validate,
    Evaluate,
    Approve,
}
Different stages have different signer sets and quorum requirements.

LCE Validation Proofs

Store LCE Proof

Store Last Committed Event validation proof:
db.set_lce_validation_proof(&subject_id, proof)?;

Get LCE Proof

Retrieve LCE validation proof:
let lce_proof = db.get_lce_validation_proof(&subject_id)?;

Delete LCE Proof

Remove LCE validation proof:
db.del_lce_validation_proof(&subject_id)?;
Implemented in core/src/database/db.rs:403-423.

Genesis Event Validation

Special handling for genesis events:
if new_proof.sn == 0 {
    if new_proof.governance_version != new_proof.genesis_governance_version {
        return Err(ValidationError::GenesisGovVersionsDoesNotMatch(
            new_proof.subject_id.to_str(),
        ));
    }
    Ok(new_proof.subject_public_key.clone())
}
See core/src/validation/validation.rs:217-224.

Error Handling

Validation errors:
pub enum ValidationError {
    GovernanceNotFound,
    GovernanceVersionTooHigh,
    GovernanceVersionTooLow,
    SubjectSignatureNotValid,
    EventSnLowerThanLastSigned,
    DifferentProofForEvent,
    PreviousProofLeft,
    GenesisGovVersionsDoesNotMatch(String),
    InvalidSignature,
    InvalidSigner,
    QuorumNotReached,
    DatabaseError,
    ChannelError(ChannelErrors),
    GovernanceError(RequestError),
    GovApiUnexpectedResponse,
    ProtocolErrors(SubjectError),
}
Handle errors appropriately:
match validation.validation_event(event, sender).await {
    Ok(response) => {
        // Process successful validation
    },
    Err(ValidationError::GovernanceVersionTooLow) => {
        // Governance update needed
    },
    Err(ValidationError::QuorumNotReached) => {
        // Wait for more signatures
    },
    Err(e) => {
        eprintln!("Validation error: {:?}", e);
    }
}

Best Practices

  1. Proof Storage: Always store validation proofs for audit trails
  2. Governance Sync: Keep governance versions synchronized across nodes
  3. Signature Verification: Verify all signatures before accepting proofs
  4. Quorum Monitoring: Track quorum progress for transparency
  5. Error Recovery: Implement retry logic for governance version mismatches
  6. Database Backups: Regular backups of validation registers
  7. Performance: Index validation proofs by subject for fast retrieval
Validation proofs are cryptographic guarantees of event integrity. Never bypass validation checks in production.

Validation Response

Successful validation returns:
pub struct ValidationEventResponse {
    pub validation_signature: Signature,
    pub gov_version_validation: u64,
}
This signature is sent back to the event owner to complete the validation quorum.

Build docs developers (and LLMs) love