penumbra_sdk_custody_ledger_usb/
lib.rs1mod device;
7
8use std::{ops::DerefMut, sync::Arc};
9
10use device::Device;
11use penumbra_sdk_custody::AuthorizeRequest;
12use penumbra_sdk_keys::{keys::AddressIndex, Address, FullViewingKey};
13use penumbra_sdk_proto::custody::v1::{self as pb, AuthorizeResponse};
14use penumbra_sdk_transaction::{AuthorizationData, TransactionPlan};
15use serde::{Deserialize, Serialize};
16use tokio::sync::{Mutex, MutexGuard};
17use tonic::{async_trait, Request, Response, Status};
18
19#[derive(Default)]
21pub struct InitOptions {}
22
23#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
25pub struct Config {}
26
27impl Config {
28 pub async fn initialize(_opts: InitOptions) -> anyhow::Result<Self> {
30 Ok(Self {})
33 }
34}
35
36pub struct Service {
38 device: Arc<Mutex<Option<Device>>>,
39}
40
41impl Service {
42 pub fn new(_config: Config) -> Self {
43 Self {
44 device: Arc::new(Default::default()),
45 }
46 }
47
48 async fn acquire_device(&self) -> anyhow::Result<impl DerefMut<Target = Device> + '_> {
53 let mut guard = self.device.lock().await;
54 if guard.is_none() {
55 *guard = Some(Device::connect_to_first().await?);
56 }
57 let out = MutexGuard::map(guard, |x| x.as_mut().expect("device should be initialized"));
58 Ok(out)
59 }
60
61 pub async fn impl_export_full_viewing_key(&self) -> anyhow::Result<FullViewingKey> {
66 self.acquire_device().await?.get_fvk().await
67 }
68
69 pub async fn impl_confirm_address(&self, index: AddressIndex) -> anyhow::Result<Address> {
73 self.acquire_device().await?.confirm_addr(index).await
74 }
75
76 pub async fn impl_authorize(&self, plan: TransactionPlan) -> anyhow::Result<AuthorizationData> {
78 self.acquire_device().await?.authorize(plan).await
79 }
80}
81
82#[async_trait]
83impl pb::custody_service_server::CustodyService for Service {
84 async fn authorize(
85 &self,
86 request: Request<pb::AuthorizeRequest>,
87 ) -> Result<Response<AuthorizeResponse>, Status> {
88 let request: AuthorizeRequest = request
89 .into_inner()
90 .try_into()
91 .map_err(|e: anyhow::Error| Status::invalid_argument(e.to_string()))?;
92
93 let authorization_data = self
94 .impl_authorize(request.plan)
95 .await
96 .map_err(|e| Status::unauthenticated(format!("{e:#}")))?;
97
98 let authorization_response = AuthorizeResponse {
99 data: Some(authorization_data.into()),
100 };
101
102 Ok(Response::new(authorization_response))
103 }
104
105 async fn authorize_validator_definition(
106 &self,
107 _request: Request<pb::AuthorizeValidatorDefinitionRequest>,
108 ) -> Result<Response<pb::AuthorizeValidatorDefinitionResponse>, Status> {
109 unimplemented!("ledger does not support validator operations")
110 }
111
112 async fn authorize_validator_vote(
113 &self,
114 _request: Request<pb::AuthorizeValidatorVoteRequest>,
115 ) -> Result<Response<pb::AuthorizeValidatorVoteResponse>, Status> {
116 unimplemented!("ledger does not support validator operations")
117 }
118
119 async fn export_full_viewing_key(
120 &self,
121 _request: Request<pb::ExportFullViewingKeyRequest>,
122 ) -> Result<Response<pb::ExportFullViewingKeyResponse>, Status> {
123 let fvk = self
124 .impl_export_full_viewing_key()
125 .await
126 .map_err(|e| Status::internal(format!("{}", e)))?;
127 Ok(Response::new(pb::ExportFullViewingKeyResponse {
128 full_viewing_key: Some(fvk.into()),
129 }))
130 }
131
132 async fn confirm_address(
133 &self,
134 request: Request<pb::ConfirmAddressRequest>,
135 ) -> Result<Response<pb::ConfirmAddressResponse>, Status> {
136 let address_index = request
137 .into_inner()
138 .address_index
139 .ok_or_else(|| {
140 Status::invalid_argument("missing address index in confirm address request")
141 })?
142 .try_into()
143 .map_err(|e| {
144 Status::invalid_argument(format!(
145 "invalid address index in confirm address request: {e:#}"
146 ))
147 })?;
148 let address = self
149 .impl_confirm_address(address_index)
150 .await
151 .map_err(|e| Status::internal(format!("{}", e)))?;
152
153 Ok(Response::new(pb::ConfirmAddressResponse {
154 address: Some(address.into()),
155 }))
156 }
157}