linera-io/linera-protocol · error

Burn requires an authenticated signer

Error message

Burn requires an authenticated signer

What it means

initiate_burn (the Burn operation handler) requires an authenticated signer: it expects runtime.authenticated_owner() to return Some, meaning the transaction must be signed by the owner of the chain the Burn is submitted from. The signer identifies whose wrapped tokens are burned and who receives the EVM-side refund target accounting. A panic aborts the burn transaction entirely.

Source

Thrown at linera-bridge/contracts/evm-bridge/src/contract.rs:329

        self.runtime
            .call_application(true, fungible_app_id, &mint_op);
    }

    /// Drives a user-initiated burn. Runs on the *user's* chain: moves `amount`
    /// of the authenticated signer's wrapped tokens into the signer's own escrow
    /// account on the bridge chain (a tracked transfer via the bridge's
    /// `fungible_app_id` parameter) and, in the same outgoing bundle, sends a
    /// tracked [`BridgeMessage::Burn`] to the bridge chain. Both messages share
    /// one bundle, so if the burn is rejected the funding transfer bounces back
    /// and the signer is refunded. `.with_authentication()` propagates the
    /// signer to the bridge chain, so `execute_burn` there burns the very escrow
    /// account this signer funded.
    fn initiate_burn(&mut self, amount: U128, evm_target: [u8; 20]) {
        assert!(amount.0 > 0, "Burn amount must be non-zero");
        let signer = self
            .runtime
            .authenticated_owner()
            .expect("Burn requires an authenticated signer");
        let params = self.runtime.application_parameters();
        let bridge_chain_id = params.bridge_chain_id;

        // A burn must originate from a *user* chain distinct from the bridge
        // chain. The atomic funding-then-burn bundle relies on a tracked
        // cross-chain `Credit` that bounces — refunding the signer — if the
        // burn is rejected. On the bridge chain itself the funding `Transfer`
        // is a local credit with no Credit message to bounce, so a rejected
        // self-delivered burn would strand the escrow in the signer's account.
        // Refuse to run that unsafe path: a same-chain burn is a no-op.
        if self.runtime.chain_id() == bridge_chain_id {
            log::warn!(
                "ignoring Burn submitted on the bridge chain; \
                 burns must originate from a separate user chain"
            );
            return;
        }
        let fungible_app_id = params.fungible_app_id.with_abi::<WrappedFungibleTokenAbi>();

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Submit Burn with the user chain owner's key so authenticated_owner() returns Some
  2. Verify the client's signer configuration (linera wallet / your SDK) before sending burn bundles
  3. Do not route Burn through session calls from other applications
  4. If you maintain the contract, convert the expect to assert! with the missing-signer message for a cleaner rejection

Example fix

// before
client.submit(user_chain, BridgeOperation::Burn { amount, evm_target }) // unauthenticated block
// -> transaction aborts: Burn requires an authenticated signer

// after
client
  .with_signer(owner_key) // sign the block containing the Burn operation
  .submit(user_chain, BridgeOperation::Burn { amount, evm_target });
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the burn is submitted in an owner-signed block of the user chain:
assert_eq!(wallet.owner_of(user_chain_id), Some(signer.public()));
client
    .with_signer(signer)
    .submit(user_chain_id, BridgeOperation::Burn { amount, evm_target });

Try / catch

// Burn aborts atomically including its bundled transfers; on rejection,
// verify the signer configuration and resubmit. Do not strip the signature
// requirement or route through sessions.

Prevention

When it happens

Trigger: Submitting Burn from a block not signed by the user-chain owner; invoking Burn through a session from another application (no authenticated signer); a client bug that submits the operation anonymously; replaying a burn bundle on a different chain with different ownership.

Common situations: Wallet apps that build unauthenticated operations by default; scripts using a watching-only key; test harnesses submitting Burn with generated keys on chains owned by other keys.

Understand the failure class

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/7d2f9b8a93d4552e. Report an issue: GitHub.