Tempo Documentation

repository·main·Indexed 21 days ago

https://github.com/tempoxyz/tempo

A high-performance EVM-compatible blockchain designed for large-scale stablecoin payments, featuring dedicated transaction lanes, fee sponsorship, and native stablecoin gas payments. Documentation covers the tempo-alloy Rust crate for network interaction, TIP20 precompile bindings, TempoAccountsWallet for transaction signing, sponsored transactions, and the Tempo CLI extension system.

Tokens
176.9K
Snippets
430
Records
718
Agent score
73%

What's inside Tempo

  1. Understand the `tempo-e2e` crate

    main

    The tempo-e2e crate provides end-to-end (e2e) tests for Tempo networks. These tests spin up entire networks of validators, including both the consensus and execution layers, to verify that the network can successfully reach a minimum required block height.

    Runtime Architecture

    • Consensus Layer: Runs within a commonware_runtime::deterministic environment to ensure predictable consensus behavior.
    • Execution Layer: Runs within a non-deterministic tokio runtime.

    Because these two layers must interact, the tests use a paced approach: the deterministic consensus runtime waits in real time for futures running inside the tokio execution runtime to complete, rather than using simulated time.

  2. Understand the T4 Hardfork changes

    main

    The T4 hardfork is a coordinated rollout of bug fixes and security hardening changes in Tempo. These changes alter state-function behavior and gas semantics. Key areas of impact include:

    • Security Hardening: Improved recipient authorization in cancel_stale_order, explicit pause checks for internal-balance DEX debits, and pre-validation of call scopes to prevent DOS vectors.
    • Gas & Resource Management: New surcharges for scoped key authorizations, updated pricing for scope set length resets, and changes to how gas is pre-charged for cold storage/account loads.
    • Error Handling: is_initialized() now propagates Out-of-Gas (OOG) errors instead of masking them as TIP20Error::uninitialized().
    • Optimizations: Redundant SLOAD operations on packed-struct stores are skipped, and empty-recipient deletes in selector-any-recipient scopes are optimized.
  3. Understand the T5 Hardfork changes in TIP-1057

    main

    TIP-1057 is a Meta TIP that bundles several bug fixes, security hardening measures, and infrastructure changes that activate at the T5 protocol version. These changes are coordinated to ensure correctness in state-function behavior and chain state at the activation boundary.

    Key areas affected by the T5 hardfork include:

    • Precompile Storage Codegen: Fixes for fixed-size array packing logic.
    • Dynamic Storage Types: Improvements to how stale data is handled when shrinking dynamic types like Vec<T>, String, or Bytes.
    • Cross-chain Interoperability: Deployment of standard factory contracts at the T5 boundary.
  4. Overview of Tempo Transaction features

    main

    The Tempo transaction type (based on EIP-2718) introduces several native protocol features designed to improve user experience and scalability compared to standard secp256k1/sequential nonce models. Key capabilities include:

    • WebAuthn/P256 signature validation: Enables the use of passkey accounts.
    • Parallelizable nonces: Increases transaction throughput for individual accounts by removing the requirement for strictly sequential nonces.
    • Gas sponsorship: Allows applications to pay gas fees on behalf of their users.
    • Call batching: Enables efficient and atomic multicalls.
    • Scheduled transactions: Allows users to define a specific time window for transaction execution.
    • Access keys: Enables a primary sender key to provision scoped access keys with defined spending limits.
  5. ValidatorConfig V2 Migration and Initialization Rules

    main

    The transition from V1 to V2 follows a gated migration process to ensure state continuity and security:

    Initialization Gating

    • State Transitions: The initialized flag transitions only from false to true.
    • Mutator Blocking: Post-initialization mutators are blocked before initialization; migration mutators are blocked after initialization is complete.
    • Migration Completion: All V1 indices must be processed (either migrated or skipped) before initializeIfMigrated() can proceed. The migrationSkippedCount must be monotonically non-decreasing.

    Data Continuity

    • DKG Continuity: Upon successful execution of initializeIfMigrated, the value returned by getNextFullDkgCeremony() in V2 must match the value read from V1 at the time of migration.
    • Owner Bootstrap: If the V2 owner is unset during the first migration call, the owner is copied from V1. This copied owner is then used for all subsequent migration authorization checks.
  6. Understand Virtual Addresses for TIP-20 Deposit Forwarding

    main

    TIP-1022 introduces virtual addresses, which are reserved 20-byte addresses used to auto-forward TIP-20 deposits to a registered master wallet. This mechanism is designed for entities like exchanges or payment processors to provide unique deposit addresses to users without the operational cost of 'sweep' transactions or the gas cost of creating new onchain accounts (250,000 gas per new account).

    Key behaviors:

    • Auto-forwarding: When a TIP-20 transfer targets a virtual address, the protocol credits the registered master wallet instead of the literal address.
    • No State Bloat: Virtual addresses do not create onchain state; they exist as a reserved namespace.
    • Scope: This applies only to TIP-20 precompile recipient resolution. It does not apply to standard ERC-20 contracts deployed on Tempo (those behave as standard EVM transfers and may result in irrecoverable funds if sent to a virtual address) or non-transfer methods like approve, burn, or permit.
    Virtual address layout:
    [4-byte masterId] [10-byte MAGIC] [6-byte userTag]
  7. Understand the Dynamic Base Fee mechanism (TIP-1067)

    main

    TIP-1067 introduces a dynamic base_fee mechanism to Tempo, replacing the fixed base fee defined in TIP-1010. The base fee adjusts based on gas usage relative to a GAS_TARGET to manage network congestion.

    Core Invariants

    • Range Clamp: The base_fee is always constrained such that BASE_FEE_FLOOR ≤ base_fee ≤ BASE_FEE_CAP.
    • Floor and Cap: The BASE_FEE_FLOOR is defined as BASE_FEE_CAP / 20. The BASE_FEE_CAP is set lower than the fixed base fee from TIP-1010, ensuring the dynamic fee never exceeds the previous fixed fee model.
    • Monotonic Response:
      • If parent_gas_used > GAS_TARGET, then base_fee ≥ parent_base_fee.
      • If parent_gas_used < GAS_TARGET, then base_fee ≤ parent_base_fee.
      • If parent_gas_used == GAS_TARGET, then base_fee == parent_base_fee (subject to clamping).
    • Determinism: The base_fee is a pure function of the parent block's base fee and gas used, ensuring all honest nodes compute the same value.
    • Header Validation: A block is only valid if its baseFeePerGas header field exactly matches the computed base_fee.
    • Activation: The base fee of the activation block is initialized to BASE_FEE_CAP.
  8. Restrict Access Key calls using CallScope

    main

    You can restrict an Access Key so it can only interact with specific contracts and specific functions using CallScope. This prevents a compromised key from being used to call arbitrary functions on other contracts.

    Scoping Logic

    • Target Scoping: Defines which address the key is allowed to call.
    • Selector Scoping: Within a target, you can define SelectorRules to restrict which function selectors are allowed.
    • Empty Selector Rules: If selector_rules is an empty array [], the key is allowed to call any selector on that specific target.
    • Explicit Selector Rules: If selector_rules contains entries, the key can only call the listed selectors.

    Recipient-Bound Token Calls

    For specific token methods, you can further restrict the recipients (the first address argument in the ABI). This is useful for ensuring a key can only transfer tokens to a pre-approved list of addresses.

    Supported constrained selectors:

    • 0xa9059cbb (transfer(address,uint256))
    • 0x095ea7b3 (approve(address,uint256))
    • 0x95777d59 (transferWithMemo(address,uint256,bytes32))
    // Example: Allow only 'swap' and 'exactInput' on a specific DEX
    CallScope memory dexScope = CallScope({
        target: DEX_ADDRESS,
        selectorRules: [
            SelectorRule({ selector: 0x...swap, recipients: [] }),
            SelectorRule({ selector: 0x...exactInput, recipients: [] })
        ]
    });
    
    // Example: Allow 'transfer' ONLY to a specific receiver
    CallScope memory tokenScope = CallScope({
        target: TOKEN_ADDRESS,
        selectorRules: [
            SelectorRule({
                selector: 0xa9059cbb, // transfer
                recipients: [0xReceiverAddress]
            })
        ]
    });
  9. Master ID Registration and Security

    main

    A masterId is a 4-byte identifier derived from a registration process. To prevent hijacking of masterIds, the registration requires a 32-bit Proof-of-Work (PoW).

    Registration Logic

    • Derivation: The masterId is derived from keccak256(abi.encodePacked(registrationAddress, salt)). Specifically, it is the bytes at [4:8] of the resulting hash.
    • PoW Requirement: The first 4 bytes of the registrationHash must be zero. If this check fails, the registration reverts with ProofOfWorkFailed.
    • Immutability: Once a masterId is registered to a master address, the registration is immutable.

    Security Model

    Security relies on the difficulty of finding a (attackerAddress, salt) pair that simultaneously satisfies the 32-bit PoW and results in the same 4-byte masterId. This requires approximately $2^{64}$ work, making targeted attacks computationally expensive.

  10. Concept: How Expiring Nonces work (Circular Buffer)

    main

    Expiring nonces use a circular buffer to manage replay protection efficiently without permanent state growth.

    Mechanism:

    1. Storage: The protocol maintains a fixed-capacity circular buffer (EXPIRING_NONCE_SET_CAPACITY = 300,000) and a mapping of transaction hashes to their expiry timestamps (expiringNonceSeen).
    2. Replay Check: When a transaction is processed, the protocol checks if its hash exists in expiringNonceSeen with an expiry timestamp greater than the current time. If so, it is rejected as a replay.
    3. Insertion & Eviction: The transaction hash is added to the circular buffer at the current pointer position. If the buffer slot is occupied by an oldHash that hasn't expired yet, the transaction is rejected to prevent buffer overflow of valid entries. Otherwise, the old entry is evicted, and the new hash/expiry is stored.
    4. Pointer Management: A pointer (expiringNonceRingPtr) advances with every insertion, wrapping around via modulo arithmetic.

    This design ensures that storage remains constant and ephemeral, as entries are overwritten once the buffer cycles or they expire.

  11. Use Witnesses in Key Authorizations to bind signatures to application context

    main

    A witness is an optional 32-byte field added to the key_authorization payload. It allows a user to sign a single payload that simultaneously authorizes a new access key and proves identity to an offchain verifier (like an application server) by binding the signature to a specific context, such as a server-issued challenge.

    How it works

    1. Application Challenge: An application server issues a challenge (e.g., a random nonce).
    2. Client Signing: The client creates a key_authorization and includes the challenge (or a digest of it) as the witness field. The client signs the RLP-encoded payload.
    3. Offchain Verification: The application server receives the key_authorization and the signature. It RLP-decodes the payload, recomputes the expected witness from its issued challenge, and asserts that decoded_witness == expected_witness. It then verifies the signature against the keccak256 hash of the encoded payload.
    4. Onchain Registration: The authorization is submitted to the protocol. If the (account, witness) pair has not been manually burned, the authorization is registered. The protocol emits an event exposing the (account, witness) pair upon success.

    Note: The protocol does not consume or burn the witness upon a successful onchain authorization. The witness remains valid for offchain verification purposes.

    # Offchain Verification Flow
    1. Split payload into key_authorization and signature
    2. RLP-decode key_authorization
    3. Assert decoded_witness == expected_witness (from server challenge)
    4. Verify signature over keccak256(key_authorization)
    5. Validate other fields (expiry, limits, etc.) against policy