{"record":{"id":"e5e821565052e83f","repo":"linera-io/linera-protocol","slug":"setrpcendpoint-requires-an-authenticated-signer","errorCode":null,"errorMessage":"SetRpcEndpoint requires an authenticated signer","messagePattern":"SetRpcEndpoint requires an authenticated signer","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/contracts/evm-bridge/src/contract.rs","lineNumber":105,"sourceCode":"                        .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    }\n\n    async fn execute_message(&mut self, message: BridgeMessage) {\n        match message {\n            BridgeMessage::Burn { amount, evm_target } => {\n                // A bouncing delivery is a no-op: the funding transfer is part\n                // of the same outgoing bundle and bounces on its own, refunding\n                // the user on their chain via wrapped-fungible's Credit handler.\n                let is_bouncing = self\n                    .runtime\n                    .message_is_bouncing()","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/contracts/evm-bridge/src/contract.rs#L87-L123","documentation":"SetRpcEndpoint is owner-only: the contract expects runtime.authenticated_owner() to return Some, i.e. the executing transaction must carry the bridge-chain owner's signature. When it panics, the whole transaction aborts and the previously stored rpc_endpoint is left unchanged. Note that even after passing this gate, validate_rpc_endpoint will dial the endpoint and abort on failure.","triggerScenarios":"Submitting SetRpcEndpoint signed by a non-owner key; executing it via a cross-application session with no authenticated signer; running an ops script that authenticates with a read-only or rotated key after the owner key changed.","commonSituations":"Rotating the bridge owner key and forgetting to update deployment automation; pointing an ops pipeline at the bridge chain with a watcher wallet instead of the owner wallet; testnets where chains are created by one account and operated by another.","solutions":["Sign and submit SetRpcEndpoint with the bridge chain owner's key","Confirm the target chain is the bridge chain and the wallet actually owns it (linera wallet show)","Pre-flight the new endpoint's chain ID (see validate_rpc_endpoint) so the signed transaction does not abort after the auth check","In your own contract builds, turn the expect into an assert! for a clearer rejection"],"exampleFix":"// before\nself.runtime\n    .authenticated_owner()\n    .expect(\"SetRpcEndpoint requires an authenticated signer\");\n\n// after\nassert!(\n    self.runtime.authenticated_owner().is_some(),\n    \"SetRpcEndpoint requires an authenticated signer\"\n);\n// Caller-side: ensure the submission client signs with the bridge chain\n// owner key, e.g. linera process-and-transfer --signer <owner-key> ...","handlingStrategy":"validation","validationCode":"// Pre-flight both gates before spending a transaction:\n// 1) signer must be the bridge chain owner\nassert_eq!(wallet.owner_of(bridge_chain_id), Some(signer.public()));\n// 2) endpoint must answer with the configured chain ID\nlet id = http_post_json(&rpc_endpoint, eth_chain_id_request()).await?;\nassert_eq!(id, format!(\"0x{source_chain_id:x}\"));\nclient.submit(bridge_chain_id, BridgeOperation::SetRpcEndpoint { rpc_endpoint });","typeGuard":null,"tryCatchPattern":"// The panic aborts the SetRpcEndpoint transaction atomically; catch nothing,\n// instead inspect the block's rejected-transaction error, fix signer or\n// endpoint, and resubmit. State (old endpoint) is unchanged.","preventionTips":["Keep a runbook mapping each bridge chain to its owner key","Rotate keys through a documented handover that updates deployment automation in the same change","Pre-flight eth_chainId from the machine that will submit the operation"],"tags":["linera","authorization","owner-signature","contract","bridge","rpc-endpoint","panic"],"backgroundTag":"unauthenticated-operation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}