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
- Submit Burn with the user chain owner's key so authenticated_owner() returns Some
- Verify the client's signer configuration (linera wallet / your SDK) before sending burn bundles
- Do not route Burn through session calls from other applications
- 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
- Wallet UIs should sign burn blocks with the chain owner key by default
- Never expose Burn as an unauthenticated entry point
- Test the funding+burn bundle path in CI with a real owner signature
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- RegisterFungibleBridge requires an authenticated signer
- SetRpcEndpoint requires an authenticated signer
- failed to query chain ID from RPC endpoint
- invalid block header RLP
- receipt inclusion proof failed
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/7d2f9b8a93d4552e.
Report an issue: GitHub.