Core Concepts
Understand the fundamental concepts behind L{CORE}.
The Oracle Problem
Blockchains are deterministic - given the same inputs, every node produces the same output. This is essential for consensus, but it creates a challenge: blockchains can't natively access external data.
Traditional solutions use "oracles" - trusted third parties that feed data to smart contracts. But this introduces trust assumptions that undermine blockchain's trustless nature.
L{CORE} solves this differently: instead of trusting an oracle's word, we cryptographically prove that data came from a specific source at a specific time.
Attestation vs. Oracles
| Aspect | Traditional Oracle | L{CORE} Attestation |
|---|---|---|
| Trust model | Trust the oracle operator | Trust cryptographic proof |
| Data source | Oracle fetches and reports | Prove data came from source |
| Verification | "Oracle says X" | "Source said X, here's proof" |
| Privacy | Oracle sees all data | Data can be bucketed/encrypted |
| Centralization | Single point of failure | Verifiable by anyone |
How Attestation Works
1. zkTLS Proof
When you request an attestation, the Attestor:
- Establishes a TLS connection to the data source
- Records the cryptographic handshake
- Generates a zero-knowledge proof that:
- The connection was to the claimed server
- The response matches what was received
- The timestamp is accurate
This proof is verifiable by anyone without re-fetching the data.
2. Privacy Processing
Before storing, data goes through privacy processing:
- Bucketing: Convert
income: $75,432toincome: 50k-100k - Encryption: Encrypt sensitive fields with NaCl box
- Redaction: Remove fields that shouldn't be stored
3. Deterministic Storage
The processed claim is submitted to Cartesi, which:
- Validates the attestation proof
- Stores data in SQLite
- Produces a notice (on-chain record)
Privacy Buckets
Privacy buckets are L{CORE}'s core privacy primitive. They convert continuous values into categorical ranges.
Why Buckets?
Smart contracts often need to answer questions like:
- "Is this user's income above $50k?"
- "Is the temperature in the safe range?"
- "Is the account balance sufficient?"
They don't need exact values - just ranges. Buckets provide exactly this.
Bucket Definition
{
field: 'income',
boundaries: [0, 25000, 50000, 100000, 250000],
labels: ['<25k', '25k-50k', '50k-100k', '100k-250k', '>250k']
}
Given income: $75,432:
- Falls between 50000 and 100000
- Assigned label:
'50k-100k' - Original value never stored
Bucket Properties
- Irreversible: Can't recover exact value from bucket
- Consistent: Same value always maps to same bucket
- Configurable: Define boundaries per use case
- Verifiable: Proof includes bucket assignment
The Two-Layer Architecture
L{CORE} separates concerns into two layers:
Attestor Layer
The Attestor handles:
- External network requests
- zkTLS proof generation
- Privacy processing
- Blockchain transactions
It's non-deterministic - it interacts with the outside world.
Cartesi Layer
The Cartesi rollup handles:
- Data validation
- SQLite storage
- Query processing
- State management
It's deterministic - same inputs always produce same outputs.
Why Separation?
Cartesi rollups must be deterministic for verification. If the Cartesi code made network calls, different validators could get different responses, breaking consensus.
By separating layers:
- Attestor does the non-deterministic work (fetching, proving)
- Cartesi does deterministic work (validating, storing)
- The proof bridges the gap
Claims and Attestations
Claim
A claim is a statement about data:
- "User X has income in range Y"
- "Sensor Z reported temperature T at time S"
- "API returned response R"
Attestation
An attestation is a claim + cryptographic proof:
- The claim itself
- zkTLS proof of data source
- Timestamp
- Attestor signature
Claim Lifecycle
1. Request -> SDK calls attestor
2. Fetch -> Attestor fetches data
3. Prove -> zkTLS proof generated
4. Process -> Privacy buckets applied
5. Submit -> Transaction to InputBox
6. Store -> Cartesi stores in SQLite
7. Query -> Data available via inspect
Encryption
For data that can't be bucketed, L{CORE} provides encryption.
NaCl Box Encryption
L{CORE} uses NaCl box (Curve25519 + XSalsa20-Poly1305):
- Asymmetric encryption
- Only the key holder can decrypt
- Data stored encrypted in Cartesi
Use Cases
- SSN, passport numbers (can't bucket meaningfully)
- Medical records
- Any PII that needs full privacy
Key Management
// Generate keypair
const keypair = nacl.box.keyPair()
const publicKey = Buffer.from(keypair.publicKey).toString('base64')
const privateKey = Buffer.from(keypair.secretKey).toString('base64')
// Attest with encryption
const claim = await lcore.attest({
provider: 'http',
params: {
url: 'https://api.example.com/sensitive',
encrypt: true,
encryptionKey: publicKey
}
})
// Decrypt later
const decrypted = lcore.decrypt(claim.encryptedData, privateKey)
Providers
Providers define how to fetch and process different data types.
HTTP Provider
The most common provider - attests any HTTP endpoint:
await lcore.attest({
provider: 'http',
params: {
url: 'https://api.example.com/data',
method: 'GET',
headers: { 'Authorization': 'Bearer token' }
}
})
IoT Provider
For IoT devices with platform-specific authentication:
await lcore.attest({
provider: 'iot',
params: {
platform: 'aws-iot',
deviceId: 'sensor-001',
topic: 'sensors/temperature'
}
})
Custom Providers
Define your own provider logic for specialized sources.
Querying Data
Inspect Endpoint
Read data without blockchain transactions:
const result = await lcore.query({
type: 'attestation',
params: { claimId: 'abc123' }
})
This is:
- Free (no gas costs)
- Fast (direct HTTP)
- Read-only
Query Types
| Type | Description |
|---|---|
attestation | Get specific claim by ID |
attestations_by_user | All claims for a user |
attestations_by_provider | All claims from a provider |
provider_schemas | Available bucket definitions |
Next Steps
- First Attestation - Hands-on tutorial
- How It Works - Visual architecture
- Glossary - Term definitions