Smart Triggers

Verifiable events initiating Smart Workflow execution

Triggers can be blockchain events (contract logs, transaction states), API requests (workflow_dispatch), or time-based schedules (cron). Each type has different observation and verification patterns suited to its source.

Examples

Configuration

Trigger workflow manually via HTTP API request. Useful for on-demand execution, manual operations, or external system integrations.

Workflow Configuration
on:
  workflow_dispatch:

Trigger

HTTP POST request received at /workflows/ping/dispatches.

HTTP API Request
POST /workflows/0x4f3a7c9e2b5d8a1c4e7f0b3d6a9c2e5f8b1d4a7c0e3b6d9f2a5c8e1b4d7a0c3/dispatches

{
  "authority_signature": "0x4a7b9c3d8e2f1a5c6d9e3b7f2a5d8c1e4b7a9d3c6e2f5a8b1d4c7e9f3a6b2d5c",
  "authority_pubkey": "0x892b3c5e8d1f4a7c9e2b6d8a3f5c7e9b4d6a8c1e3b5d7a9c2e4f6b8d1a3c5e7",
  "payer_signature": "0x2f8a9d3c6e5b1f4a7c0e2b9d5a8c1e4f7b0a3d6c9e2f5a8b1d4c7e0f3a6b9d2",
  "payer_pubkey": "0x6d9a2c5e8f1b4d7a0c3e6b9d2f5a8c1e4b7d0a3c6e9f2b5d8a1c4e7f0b3d6a9",
  "timestamp": 1730485532000,
  "nonce": "0x7c3e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1b4d7a0c3e6f9b2d5a8c1e4f7b0d3a6"
}
Workflow Hash:0x4f3a7c9e2b5d8a1c4e7f0b3d6a9c2e5f8b1d4a7c0e3b6d9f2a5c8e1b4d7a0c3Name:pingAuthority:0x892b3c5e8d1f4a7c9e2b6d8a3f5c7e9b4d6a8c1e3b5d7a9c2e4f6b8d1a3c5e7Payer:0x6d9a2c5e8f1b4d7a0c3e6b9d2f5a8c1e4b7d0a3c6e9f2b5d8a1c4e7f0b3d6a9Timestamp:1730485532000Nonce:0x7c3e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1b4d7a0c3e6f9b2d5a8c1e4f7b0d3a6

Observation

Validator verifies authority and payer, creates trigger attestation.

Detection
Observation Code
// Validator's HTTP RPC handler
rpc_server.post("/workflows/:workflow_hash/dispatches", (req) => {
  const workflow_hash = req.params.workflow_hash; // Full 32-byte hash
  const {
    authority_signature, authority_pubkey,
    payer_signature, payer_pubkey,
    timestamp, nonce
  } = req.body;

  const workflow_config = get_workflow_config(workflow_hash);
  const now = Date.now();

  // Check timestamp is within acceptable range
  const MIN_TIMESTAMP = now - 5 * 60 * 1000;  // 5 min past
  const MAX_TIMESTAMP = now + 24 * 60 * 60 * 1000;  // 24 hrs future
  if (timestamp < MIN_TIMESTAMP || timestamp > MAX_TIMESTAMP) {
    return error("Timestamp out of range");
  }

  // Verify authority signature (permission)
  const auth_message = hash(workflow_hash + timestamp + nonce);
  if (!verify(auth_message, authority_signature, authority_pubkey)) {
    return error("Invalid authority signature");
  }

  // Check authority is authorized
  if (!workflow_config.authorized_keys.includes(authority_pubkey)) {
    return error("Authority not authorized");
  }

  // Verify payer signature (payment commitment)
  const payer_message = hash(workflow_hash + timestamp + nonce + authority_pubkey);
  if (!verify(payer_message, payer_signature, payer_pubkey)) {
    return error("Invalid payer signature");
  }

  // Check payer has sufficient balance (or deduct fees)
  if (!has_sufficient_balance(payer_pubkey, workflow_config.fee)) {
    return error("Insufficient payer balance");
  }

  // Create trigger with authority and payer proof
  const trigger = {
    workflow_hash,
    topic: "trigger.workflow_dispatch",
    data: {
      workflow_hash,
      timestamp,
      nonce,
      authority_signature,
      authority_pubkey,
      payer_signature,
      payer_pubkey
    }
  };

  // Validator signs and broadcasts
  broadcast(sign_message({
    message_type: "trigger",
    timestamp_ms: now,
    payload: trigger
  }));
});
Attestation
Trigger Attestation
{
  "content": {
    "message_type": "trigger",
    "timestamp_ms": 1730485532000,
    "payload": {
      "workflow_hash": "0x4f3a7c9e2b5d8a1c4e7f0b3d6a9c2e5f8b1d4a7c0e3b6d9f2a5c8e1b4d7a0c3",
      "topic": "trigger.workflow_dispatch",
      "data": {
        "workflow_hash": "0x4f3a7c9e2b5d8a1c4e7f0b3d6a9c2e5f8b1d4a7c0e3b6d9f2a5c8e1b4d7a0c3",
        "timestamp": 1730485532000,
        "nonce": "0x7c3e9f2a5d8b1c4e7f0a3d6b9c2e5f8a1b4d7a0c3e6f9b2d5a8c1e4f7b0d3a6",
        "authority_signature": "0x4a7b9c3d8e2f1a5c6d9e3b7f2a5d8c1e4b7a9d3c6e2f5a8b1d4c7e9f3a6b2d5c",
        "authority_pubkey": "0x892b3c5e8d1f4a7c9e2b6d8a3f5c7e9b4d6a8c1e3b5d7a9c2e4f6b8d1a3c5e7",
        "payer_signature": "0x2f8a9d3c6e5b1f4a7c0e2b9d5a8c1e4f7b0a3d6c9e2f5a8b1d4c7e0f3a6b9d2",
        "payer_pubkey": "0x6d9a2c5e8f1b4d7a0c3e6b9d2f5a8c1e4b7d0a3c6e9f2b5d8a1c4e7f0b3d6a9"
      }
    }
  },
  "signature": "0x8a3f9d7c2e5b1f4a6d8c3e9b7a5f2d4c8e1b6a9d3c7e2f5a8b4d1c6e9f3a7b2d",
  "signer": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2"
}

Verification

Validators verify authority and payer authorization, all three signatures valid.

Verification Protocol
1. Check Topic
topic == "trigger.workflow_dispatch"
2. Check Workflow Exists
workflow_exists(workflow_name)
3. Verify Timestamp Range
now - 5min < timestamp < now + 24hrs
4. Verify Authority Signature
verify(hash(workflow_hash || timestamp || nonce), authority_signature, authority_pubkey)
5. Check Authority Authorized
workflow.authorized_keys.contains(authority_pubkey)
6. Verify Payer Signature
verify(hash(workflow_hash || timestamp || nonce || authority_pubkey), payer_signature, payer_pubkey)
7. Check Payer Has Balance
has_sufficient_balance(payer_pubkey, workflow.fee)
8. Verify Validator Signature
verify(message.content.hash(), signature, signer)
Challenge & Fraud Proof
// Challenge if authority unauthorized or signatures invalid
fn verify_and_challenge(
    signed_message: SignedMessage
) -> Result<()> {
    let trigger = signed_message.content.payload;
    let data = trigger.data;

    // Check topic first (cheap string comparison)
    if trigger.topic != "trigger.workflow_dispatch" {
        return submit_fraud_proof(InvalidTopic);
    }

    // Check workflow exists (cheap hashmap lookup)
    let workflow = get_workflow(&trigger.workflow_hash)
        .ok_or(submit_fraud_proof(WorkflowNotFound))?;

    // Check timestamp is within range (cheap comparison)
    let now = current_timestamp_ms();
    let min_ts = now - 5 * 60 * 1000;  // 5 min past
    let max_ts = now + 24 * 60 * 60 * 1000;  // 24 hrs future
    if data.timestamp < min_ts || data.timestamp > max_ts {
        return submit_fraud_proof(TimestampOutOfRange);
    }

    // Verify authority signature (expensive crypto)
    let auth_message = hash(&trigger.workflow_hash, &data.timestamp, &data.nonce);
    if !verify(
        &auth_message,
        &data.authority_signature,
        &data.authority_pubkey
    ) {
        return submit_fraud_proof(InvalidAuthoritySignature);
    }

    // Check authority authorized (cheap array lookup)
    if !workflow.authorized_keys.contains(&data.authority_pubkey) {
        return submit_fraud_proof(UnauthorizedAuthority);
    }

    // Verify payer signature (expensive crypto)
    let payer_message = hash(
        &trigger.workflow_hash,
        &data.timestamp,
        &data.nonce,
        &data.authority_pubkey
    );
    if !verify(
        &payer_message,
        &data.payer_signature,
        &data.payer_pubkey
    ) {
        return submit_fraud_proof(InvalidPayerSignature);
    }

    // Check payer has balance (cheap balance lookup)
    if !has_sufficient_balance(&data.payer_pubkey, workflow.fee) {
        return submit_fraud_proof(InsufficientPayerBalance);
    }

    // Verify validator signature last (expensive crypto)
    if !verify_signature(
        signed_message.content.hash(),
        &signed_message.signature,
        &signed_message.signer
    ) {
        return submit_fraud_proof(InvalidValidatorSignature);
    }

    Ok(())
}

Consensus

The network reaches agreement that the trigger is valid.

validator_1✓ ATTESTED
validator_2✓ ATTESTED
validator_3✓ ATTESTED
validator_4✓ ATTESTED
validator_5○ OFFLINE
validator_6✗ CHALLENGED→ SLASHED
✓ Quorum Reached
4/6 validators attested correctly
Threshold: ≥4 required (2/3 of 6)
→ Trigger accepted, workflow begins
Honest attestors (4): Proceed with workflow execution
Offline (1): No response within timeout, no penalty
Challenged (1): Fraud proof submitted, challenger rewarded, attestor slashed