Skip to content
63 changes: 36 additions & 27 deletions backend/src/walacor_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,15 @@ def store_document_hash(self, loan_id: str, document_type: str, document_hash: s
if len(document_hash) != 64:
raise ValueError("document_hash must be a 64-character SHA-256 hash")

# HYBRID APPROACH: Only essential blockchain data goes to Walacor
# HYBRID APPROACH: Match Walacor schema fields exactly
blockchain_data = {
"loan_id": loan_id,
"document_type": document_type,
"document_hash": document_hash,
"seal_timestamp": datetime.now().isoformat(),
"etid": self.LOAN_DOCUMENTS_ETID,
"integrity_seal": f"SEAL_{document_hash[:16]}_{int(datetime.now().timestamp())}"
"file_size": file_size,
"upload_timestamp": int(datetime.now().timestamp()), # DATETIME_EPOCH format
"uploaded_by": uploaded_by,
"file_path": file_path
}

# Store in Walacor or local blockchain (only essential data)
Expand Down Expand Up @@ -500,16 +503,20 @@ def seal_loan_document(self, loan_id: str, loan_data: Dict[str, Any],
envelope_json = json.dumps(envelope, sort_keys=True, separators=(',', ':'))
document_hash = hashlib.sha256(envelope_json.encode('utf-8')).hexdigest()

# Create blockchain data for Walacor
# Create blockchain data matching loan_documents schema
# Extract uploaded_by from borrower data or use default
uploaded_by = loan_data.get("created_by", "system")
if borrower_data and "full_name" in borrower_data:
uploaded_by = borrower_data["full_name"]

blockchain_data = {
"document_hash": document_hash,
"loan_id": loan_id,
"seal_timestamp": envelope["sealed_timestamp"],
"etid": self.LOAN_DOCUMENTS_ETID,
"integrity_seal": f"LOAN_SEAL_{document_hash[:16]}_{int(datetime.now().timestamp())}",
"envelope_size": len(envelope_json),
"borrower_data_included": True,
"file_count": len(files)
"document_type": "loan_packet",
"document_hash": document_hash,
"file_size": len(envelope_json),
"upload_timestamp": int(datetime.now().timestamp()), # DATETIME_EPOCH format
"uploaded_by": uploaded_by,
"file_path": f"/loans/{loan_id}/packet.json"
}

# Store hash in Walacor blockchain or local simulation
Expand Down Expand Up @@ -627,12 +634,14 @@ def log_audit_event(self, document_id: str, event_type: str, user: str,
if not all([document_id, event_type, user]):
raise ValueError("document_id, event_type, and user are required")

# HYBRID APPROACH: Only essential audit data goes to blockchain
# Match audit_logs schema fields exactly
blockchain_audit_data = {
"document_id": document_id,
"event_type": event_type,
"audit_timestamp": datetime.now().isoformat(),
"audit_hash": f"AUDIT_{document_id}_{event_type}_{int(datetime.now().timestamp())}"
"user": user,
"timestamp": int(datetime.now().timestamp()), # DATETIME_EPOCH format
"ip_address": ip_address if ip_address else "",
"details": details if details else ""
}

# Store in Walacor or local blockchain (only essential audit data)
Expand Down Expand Up @@ -774,13 +783,13 @@ def create_provenance_link(self, parent_doc_id: str, child_doc_id: str,
if parent_doc_id == child_doc_id:
raise ValueError("parent_doc_id and child_doc_id must be different")

# Prepare provenance data
# Match document_provenance schema fields exactly
provenance_data = {
"parent_doc_id": parent_doc_id,
"child_doc_id": child_doc_id,
"relationship_type": relationship_type,
"timestamp": datetime.now().isoformat(),
"description": description
"timestamp": int(datetime.now().timestamp()), # DATETIME_EPOCH format
"description": description if description else ""
}

# Store in Walacor
Expand Down Expand Up @@ -821,23 +830,23 @@ def create_attestation(self, document_id: str, attestor_name: str,
RuntimeError: If Walacor operation fails
"""
try:
# Validate inputs
if not all([document_id, attestor_name, attestation_type, status, signature]):
raise ValueError("document_id, attestor_name, attestation_type, status, and signature are required")
# Validate required inputs (signature and notes are optional per schema)
if not all([document_id, attestor_name, attestation_type, status]):
raise ValueError("document_id, attestor_name, attestation_type, and status are required")

valid_statuses = ["pending", "approved", "rejected"]
if status not in valid_statuses:
raise ValueError(f"status must be one of: {', '.join(valid_statuses)}")
# Prepare attestation data

# Match attestations schema fields exactly
attestation_data = {
"document_id": document_id,
"attestor_name": attestor_name,
"attestation_type": attestation_type,
"status": status,
"timestamp": datetime.now().isoformat(),
"signature": signature,
"notes": notes
"timestamp": int(datetime.now().timestamp()), # DATETIME_EPOCH format
"signature": signature if signature else "",
"notes": notes if notes else ""
}

# Store in Walacor
Expand Down
155 changes: 155 additions & 0 deletions scripts/check_walacor_schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/usr/bin/env python
"""
Check Walacor Schemas Script

This script lists all existing schemas on your Walacor tenant and checks
if the required schemas for IntegrityX already exist.
"""

import os
import sys
from pathlib import Path
from dotenv import load_dotenv

# Add backend/src directory to Python path
script_dir = Path(__file__).parent
project_root = script_dir.parent
backend_src_dir = project_root / "backend" / "src"
sys.path.insert(0, str(backend_src_dir))

try:
from walacor_sdk import WalacorService
except ImportError as e:
print(f"❌ Import Error: {e}")
print("Please ensure walacor-python-sdk is installed")
sys.exit(1)


def main():
print("=" * 70)
print("WALACOR SCHEMA CHECKER")
print("=" * 70)
print()

# Load environment
env_path = project_root / "backend" / ".env"
if not env_path.exists():
print(f"❌ .env file not found at: {env_path}")
sys.exit(1)

load_dotenv(env_path)

host = os.getenv('WALACOR_HOST')
username = os.getenv('WALACOR_USERNAME')
password = os.getenv('WALACOR_PASSWORD')

if not all([host, username, password]):
print("❌ Missing Walacor credentials in .env")
sys.exit(1)

# Connect to Walacor
print(f"📡 Connecting to Walacor at: {host}")
try:
wal = WalacorService(
server=f"http://{host}/api",
username=username,
password=password
)

schemas = wal.schema.get_list_with_latest_version()
print(f"✅ Connected successfully!")
print(f" Found {len(schemas)} schemas on tenant")
print()

except Exception as e:
print(f"❌ Failed to connect: {e}")
sys.exit(1)

# Required schemas for IntegrityX
required_schemas = {
100001: "loan_documents",
100002: "document_provenance",
100003: "attestations",
100004: "audit_logs"
}

# Check for meta-schema (needed to create schemas)
print("🔍 Checking for Schema Creation Meta-Schema:")
meta_schema_found = False
for schema in schemas:
schema_data = schema.model_dump()
if schema_data.get('ETId') == 50:
print(f" ✅ Found ETId 50 (Schema Creation) - SV {schema_data.get('SV', 'Unknown')}")
meta_schema_found = True
break

if not meta_schema_found:
print(f" ❌ ETId 50 (Schema Creation Meta-Schema) NOT FOUND")
print(f" 💡 This is why schema creation is failing")
print()

# Check for required IntegrityX schemas
print("🔍 Checking for Required IntegrityX Schemas:")
found_schemas = {}

for schema in schemas:
schema_data = schema.model_dump()
etid = schema_data.get('ETId')

if etid in required_schemas:
table_name = schema_data.get('TableName', 'Unknown')
sv = schema_data.get('SV', 'Unknown')
found_schemas[etid] = (table_name, sv)
print(f" ✅ ETId {etid} ({required_schemas[etid]}) - Found as '{table_name}' (SV {sv})")

# Check for missing schemas
missing_schemas = set(required_schemas.keys()) - set(found_schemas.keys())

if missing_schemas:
print()
print("⚠️ Missing Required Schemas:")
for etid in sorted(missing_schemas):
print(f" ❌ ETId {etid} ({required_schemas[etid]}) - NOT FOUND")

print()
print("=" * 70)
print("ALL SCHEMAS ON TENANT:")
print("=" * 70)

# List all schemas
for i, schema in enumerate(schemas, 1):
schema_data = schema.model_dump()
etid = schema_data.get('ETId', 'Unknown')
sv = schema_data.get('SV', 'Unknown')
table_name = schema_data.get('TableName', 'Unknown')
family = schema_data.get('Family', 'Unknown')

print(f"{i:2}. ETId={etid:<6} SV={sv:<3} Table={table_name:<30} Family={family}")

print()
print("=" * 70)
print("SUMMARY:")
print("=" * 70)

if not meta_schema_found:
print("❌ Cannot create schemas programmatically (ETId 50 missing)")
print("💡 RECOMMENDATION: Create schemas manually via Walacor Dashboard")
elif missing_schemas:
print(f"⚠️ {len(missing_schemas)} required schemas are missing")
print("💡 RECOMMENDATION: Run initialize_schemas.py to create them")
else:
print("✅ All required schemas exist!")
print("🎉 Your IntegrityX system is ready to use")


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n⚠️ Interrupted by user")
sys.exit(1)
except Exception as e:
print(f"\n\n❌ Unexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
2 changes: 1 addition & 1 deletion scripts/initialize_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from schemas import LoanSchemas
except ImportError as e:
print(f"❌ Import Error: {e}")
print("Please ensure walacor-python-sdk is installed and src/schemas.py exists")
print("Please ensure walacor-python-sdk is installed and backend/src/schemas.py exists")
sys.exit(1)


Expand Down
Loading