{"record":{"id":"16fb9d21d6de4107","repo":"linera-io/linera-protocol","slug":"registerfungiblebridge-requires-an-authenticated-s","errorCode":null,"errorMessage":"RegisterFungibleBridge requires an authenticated signer","messagePattern":"RegisterFungibleBridge requires an authenticated signer","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/contract.rs","lineNumber":95,"sourceCode":"                )\n                .await;\n            }\n            BridgeOperation::VerifyBlockHash { block_hash } => {\n                self.verify_block_hash(block_hash).await;\n\n                // Only cache when called by an authenticated signer (chain owner),\n                // preventing unauthenticated callers from bloating state.\n                if self.runtime.authenticated_owner().is_some() {\n                    self.state\n                        .verified_block_hashes\n                        .insert(&block_hash)\n                        .expect(\"failed to insert verified block hash\");\n                }\n            }\n            BridgeOperation::RegisterFungibleBridge { address } => {\n                self.runtime\n                    .authenticated_owner()\n                    .expect(\"RegisterFungibleBridge requires an authenticated signer\");\n                assert!(\n                    self.state.bridge_contract_address.get().is_none(),\n                    \"bridge contract address is already registered and cannot be changed\"\n                );\n                self.state.bridge_contract_address.set(Some(address));\n            }\n            BridgeOperation::SetRpcEndpoint { rpc_endpoint } => {\n                self.runtime\n                    .authenticated_owner()\n                    .expect(\"SetRpcEndpoint requires an authenticated signer\");\n                self.validate_rpc_endpoint(&rpc_endpoint).await;\n                self.state.rpc_endpoint.set(rpc_endpoint);\n            }\n            BridgeOperation::Burn { amount, evm_target } => {\n                self.initiate_burn(amount, evm_target);\n            }\n        }\n    }","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/contract.rs#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (contract panics, whole transaction aborts)\nself.runtime\n    .authenticated_owner()\n    .expect(\"RegisterFungibleBridge requires an authenticated signer\");\n\n// after (clean rejection, same semantics)\nlet Some(_owner) = self.runtime.authenticated_owner() else {\n    panic!(\"RegisterFungibleBridge requires an authenticated signer\");\n};\n// Caller-side fix: submit the operation in an owner-signed block —\n// linera process-and-transfer --signer <owner-key> <bridge-chain> ...","handlingStrategy":"validation","validationCode":"// Before submitting, confirm the client will sign as the bridge chain owner:\nlet owner = wallet.owner_of(bridge_chain_id).expect(\"not the owner of bridge chain\");\nassert_eq!(owner, signer.public());\nclient.submit(bridge_chain_id, BridgeOperation::RegisterFungibleBridge { address });","typeGuard":"// Rust narrowing helper for contract code:\nfn require_authenticated_owner(runtime: &mut impl ContractRuntime)\n    -> Result<Owner, &'static str>\n{\n    runtime.authenticated_owner().ok_or(\"RegisterFungibleBridge requires an authenticated signer\")\n}","tryCatchPattern":"// Linera transactions abort atomically on panic; there is nothing to catch\n// client-side beyond the failed transaction. Check the failure reason and\n// re-sign the operation with the owner key before resubmitting.","preventionTips":["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"],"tags":["linera","authorization","owner-signature","contract","bridge","panic"],"backgroundTag":"unauthenticated-operation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}