linera-io/linera-protocol · error
RegisterFungibleBridge requires an authenticated signer
Error message
RegisterFungibleBridge requires an authenticated signer
What it means
RegisterFungibleBridge is an owner-only operation: the contract calls runtime.authenticated_owner() and expects Some, which Linera only returns when the transaction executing this operation is signed by the owner of the bridge chain. A panic aborts the entire transaction, so bridge_contract_address is never set. This is the intended authorization gate for binding the bridge to an EVM contract address.
Source
Thrown at linera-bridge/contracts/evm-bridge/src/contract.rs:95
)
.await;
}
BridgeOperation::VerifyBlockHash { block_hash } => {
self.verify_block_hash(block_hash).await;
// Only cache when called by an authenticated signer (chain owner),
// preventing unauthenticated callers from bloating state.
if self.runtime.authenticated_owner().is_some() {
self.state
.verified_block_hashes
.insert(&block_hash)
.expect("failed to insert verified block hash");
}
}
BridgeOperation::RegisterFungibleBridge { address } => {
self.runtime
.authenticated_owner()
.expect("RegisterFungibleBridge requires an authenticated signer");
assert!(
self.state.bridge_contract_address.get().is_none(),
"bridge contract address is already registered and cannot be changed"
);
self.state.bridge_contract_address.set(Some(address));
}
BridgeOperation::SetRpcEndpoint { rpc_endpoint } => {
self.runtime
.authenticated_owner()
.expect("SetRpcEndpoint requires an authenticated signer");
self.validate_rpc_endpoint(&rpc_endpoint).await;
self.state.rpc_endpoint.set(rpc_endpoint);
}
BridgeOperation::Burn { amount, evm_target } => {
self.initiate_burn(amount, evm_target);
}
}
}View on GitHub (pinned to 6c226ddcb3)
Solutions
- Submit the operation from a client that signs the block with the bridge chain owner's key
- Verify wallet ownership before deploying (e.g. linera wallet show / your client's signing config) and confirm you are targeting the bridge chain ID
- If you maintain the contract, prefer assert!(authenticated_owner().is_some(), ...) so the rejection message is a clean transaction failure
- Re-check that no middleware/session wrapper strips the owner signature from the transaction
Example fix
// before (contract panics, whole transaction aborts)
self.runtime
.authenticated_owner()
.expect("RegisterFungibleBridge requires an authenticated signer");
// after (clean rejection, same semantics)
let Some(_owner) = self.runtime.authenticated_owner() else {
panic!("RegisterFungibleBridge requires an authenticated signer");
};
// Caller-side fix: submit the operation in an owner-signed block —
// linera process-and-transfer --signer <owner-key> <bridge-chain> ... Defensive patterns
Strategy: validation
Validate before calling
// Before submitting, confirm the client will sign as the bridge chain owner:
let owner = wallet.owner_of(bridge_chain_id).expect("not the owner of bridge chain");
assert_eq!(owner, signer.public());
client.submit(bridge_chain_id, BridgeOperation::RegisterFungibleBridge { address }); Type guard
// Rust narrowing helper for contract code:
fn require_authenticated_owner(runtime: &mut impl ContractRuntime)
-> Result<Owner, &'static str>
{
runtime.authenticated_owner().ok_or("RegisterFungibleBridge requires an authenticated signer")
} Try / catch
// Linera transactions abort atomically on panic; there is nothing to catch // client-side beyond the failed transaction. Check the failure reason and // re-sign the operation with the owner key before resubmitting.
Prevention
- Load the bridge chain owner key into the submitting wallet before deployment
- Never route owner-gated operations through sessions or other applications
- Add an integration test that submits the operation owner-signed to catch auth regressions
When it happens
Trigger: Submitting RegisterFungibleBridge in a block signed by any key other than the bridge-chain owner; invoking the operation through another application's session (session calls carry no authenticated signer); deploying from a wallet whose default account is not the chain owner; targeting the wrong chain ID.
Common situations: Deployment scripts that authenticate as a service or CI account; test harnesses with freshly generated keys submitting to a bridge chain created by a different wallet; misconfigured linera client where the owner key is not loaded.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- SetRpcEndpoint requires an authenticated signer
- Burn 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/16fb9d21d6de4107.
Report an issue: GitHub.