kona_sources/signer/remote/
handler.rs1use std::sync::Arc;
2
3use alloy_primitives::{Address, B256, ChainId, SignatureError};
4use alloy_rpc_client::RpcClient;
5use alloy_signer::Signature;
6use notify::RecommendedWatcher;
7use op_alloy_rpc_types_engine::PayloadHash;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10use tokio::sync::RwLock;
11
12#[derive(Debug, Serialize)]
14#[serde(rename_all = "camelCase")]
15struct BlockPayloadArgs {
16 domain: B256,
17 chain_id: u64,
18 payload_hash: B256,
19 sender_address: Address,
20}
21
22#[derive(Debug, Deserialize)]
24struct SignResponse {
25 signature: String,
26}
27
28#[derive(Debug)]
30pub struct RemoteSignerHandler {
31 pub(super) client: Arc<RwLock<RpcClient>>,
33 pub(super) address: Address,
35 pub(super) watcher_handle: Option<RecommendedWatcher>,
37}
38
39#[derive(Debug, Error)]
41pub enum RemoteSignerError {
42 #[error("JSON-RPC transport error: {0}")]
44 SigningRPCError(#[from] alloy_transport::TransportError),
45 #[error("JSON serialization error: {0}")]
47 JsonError(#[from] serde_json::Error),
48 #[error("Failed to ping signer: {0}")]
50 PingError(alloy_transport::TransportError),
51 #[error("Invalid signature hex encoding: {0}")]
53 InvalidSignatureHex(alloy_primitives::hex::FromHexError),
54 #[error("Invalid signature length, expected 65 bytes, got {0}")]
56 InvalidSignatureLength(usize),
57 #[error("Signature error: {0}")]
59 SignatureError(#[from] SignatureError),
60 #[error(
62 "Unsafe block signer address does not match remote signer address: {unsafe_block_signer} != {remote_signer}"
63 )]
64 InvalidAddress {
65 unsafe_block_signer: Address,
67 remote_signer: Address,
69 },
70}
71
72impl RemoteSignerHandler {
73 pub const fn is_certificate_watching_enabled(&self) -> bool {
75 self.watcher_handle.is_some()
76 }
77
78 pub async fn sign_block_v1(
80 &self,
81 payload_hash: PayloadHash,
82 chain_id: ChainId,
83 sender_address: Address,
84 ) -> Result<Signature, RemoteSignerError> {
85 if sender_address != self.address {
86 return Err(RemoteSignerError::InvalidAddress {
87 unsafe_block_signer: sender_address,
88 remote_signer: self.address,
89 });
90 }
91
92 let params = BlockPayloadArgs {
93 domain: B256::ZERO,
95 chain_id,
96 payload_hash: payload_hash.0,
97 sender_address,
98 };
99
100 let response: SignResponse = {
102 self.client
103 .read()
104 .await
105 .request("opsigner_signBlockPayload", ¶ms)
106 .await
107 .map_err(RemoteSignerError::SigningRPCError)?
108 };
109
110 let signature_bytes =
112 alloy_primitives::hex::decode(response.signature.trim_start_matches("0x"))
113 .map_err(RemoteSignerError::InvalidSignatureHex)?;
114
115 if signature_bytes.len() != 65 {
116 return Err(RemoteSignerError::InvalidSignatureLength(signature_bytes.len()));
117 }
118
119 let signature = Signature::from_raw(signature_bytes.as_slice())
120 .map_err(RemoteSignerError::SignatureError)?;
121
122 Ok(signature)
123 }
124}