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. Receive Validation Request
Validators receive validation events from event owners:
let response = validation.validation_event(
validation_event,
sender
).await?;
Implemented in core/src/validation/validation.rs:46.
2. Check Governance Version
Verify governance version consistency:
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);
}
See core/src/validation/validation.rs:51-96.
3. Verify Subject Signature
Validate the subject’s signature on the proof:
if validation_event
.subject_signature
.verify(&validation_event.proof)
.is_err()
{
return Err(ValidationError::SubjectSignatureNotValid);
}
Verify proof chain continuity:
let subject_pk = self.check_proofs(
&validation_event.proof,
validation_event.previous_proof,
validation_event.prev_event_validation_signatures,
last_proof,
).await?;
Implemented in core/src/validation/validation.rs:117.
5. Store Validation Register
Save the validation proof:
self.database
.set_validation_register(
&validation_event.proof.subject_id,
&validation_event.proof
)
.map_err(|_| ValidationError::DatabaseError)?;
6. Sign and Send Response
Generate validation signature and send response:
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?;
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
- Proof Storage: Always store validation proofs for audit trails
- Governance Sync: Keep governance versions synchronized across nodes
- Signature Verification: Verify all signatures before accepting proofs
- Quorum Monitoring: Track quorum progress for transparency
- Error Recovery: Implement retry logic for governance version mismatches
- Database Backups: Regular backups of validation registers
- 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.