Skip to content

Commit 92efbbd

Browse files
committed
Switch from postcard to cbor for the Bitcoin app
This makes it easier to keep the wire protocol forward-compatible.
1 parent b12d449 commit 92efbbd

9 files changed

Lines changed: 298 additions & 83 deletions

File tree

apps/bitcoin/app/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ base58 = { package = "base58ck", version = "0.1", default-features = false }
3636
common = { package = "vnd-bitcoin-common", path = "../common", default-features = false }
3737
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
3838
hex-literal = "0.4.1"
39+
minicbor = { version = "2.2", default-features = false, features = ["alloc", "derive"] }
3940
nom = { version = "7.1.3", default-features = false, features = ["alloc"] }
40-
postcard = { version = "1.1.1", default-features = false, features = ["alloc"] }
4141
sdk = { package = "vanadium-app-sdk", path = "../../../app-sdk", default-features = false }
4242

4343
[dev-dependencies]

apps/bitcoin/app/src/handlers/get_master_fingerprint.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub fn handle_get_master_fingerprint(
66
tree: KeyTree,
77
) -> Result<Response, common::errors::Error> {
88
let fpr = crate::bip32::master_fingerprint(tree)?;
9-
Ok(Response::MasterFingerprint(fpr))
9+
Ok(Response::MasterFingerprint { fingerprint: fpr })
1010
}
1111

1212
#[cfg(test)]
@@ -17,13 +17,23 @@ mod tests {
1717
fn test_handle_get_master_fingerprint_standard() {
1818
let response =
1919
handle_get_master_fingerprint(&mut sdk::App::singleton(), KeyTree::Standard).unwrap();
20-
assert_eq!(response, Response::MasterFingerprint(0xf5acc2fdu32));
20+
assert_eq!(
21+
response,
22+
Response::MasterFingerprint {
23+
fingerprint: 0xf5acc2fdu32
24+
}
25+
);
2126
}
2227

2328
#[test]
2429
fn test_handle_get_master_fingerprint_resident() {
2530
let response =
2631
handle_get_master_fingerprint(&mut sdk::App::singleton(), KeyTree::Resident).unwrap();
27-
assert_eq!(response, Response::MasterFingerprint(0xad85d955));
32+
assert_eq!(
33+
response,
34+
Response::MasterFingerprint {
35+
fingerprint: 0xad85d955
36+
}
37+
);
2838
}
2939
}

apps/bitcoin/app/src/handlers/sign_psbt.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,9 @@ pub async fn handle_sign_psbt(app: &mut sdk::App, psbt: &[u8]) -> Result<Respons
867867
#[cfg(not(any(test, feature = "autoapprove")))]
868868
app.show_info(Icon::Success, "Transaction signed");
869869

870-
Ok(Response::PsbtSigned(partial_signatures))
870+
Ok(Response::PsbtSigned {
871+
signatures: partial_signatures,
872+
})
871873
}
872874

873875
#[cfg(test)]
@@ -907,14 +909,16 @@ mod tests {
907909
))
908910
.unwrap();
909911

910-
assert_eq!(response, Response::PsbtSigned(vec![
911-
PartialSignature {
912-
input_index: 0,
913-
signature: hex!("3045022100e55b3ca788721aae8def2eadff710e524ffe8c9dec1764fdaa89584f9726e196022012a30fbcf9e1a24df31a1010356b794ab8de438b4250684757ed5772402540f401").to_vec(),
914-
pubkey: hex!("02ee8608207e21028426f69e76447d7e3d5e077049f5e683c3136c2314762a4718").to_vec(),
915-
leaf_hash: None
916-
}
917-
]));
912+
assert_eq!(response, Response::PsbtSigned {
913+
signatures: vec![
914+
PartialSignature {
915+
input_index: 0,
916+
signature: hex!("3045022100e55b3ca788721aae8def2eadff710e524ffe8c9dec1764fdaa89584f9726e196022012a30fbcf9e1a24df31a1010356b794ab8de438b4250684757ed5772402540f401").to_vec(),
917+
pubkey: hex!("02ee8608207e21028426f69e76447d7e3d5e077049f5e683c3136c2314762a4718").to_vec(),
918+
leaf_hash: None
919+
}
920+
]
921+
});
918922
}
919923

920924
#[test]
@@ -939,14 +943,16 @@ mod tests {
939943
))
940944
.unwrap();
941945

942-
assert_eq!(response, Response::PsbtSigned(vec![
943-
PartialSignature {
944-
input_index: 0,
945-
signature: hex!("3045022100ab44f34dd7e87c9054591297a101e8500a0641d1d591878d0d23cf8096fa79e802205d12d1062d925e27b57bdcf994ecf332ad0a8e67b8fe407bab2101255da632aa01").to_vec(),
946-
pubkey: hex!("03ee2c3d98eb1f93c0a1aa8e5a4009b70eb7b44ead15f1666f136b012ad58d3068").to_vec(),
947-
leaf_hash: None
948-
}
949-
]));
946+
assert_eq!(response, Response::PsbtSigned {
947+
signatures: vec![
948+
PartialSignature {
949+
input_index: 0,
950+
signature: hex!("3045022100ab44f34dd7e87c9054591297a101e8500a0641d1d591878d0d23cf8096fa79e802205d12d1062d925e27b57bdcf994ecf332ad0a8e67b8fe407bab2101255da632aa01").to_vec(),
951+
pubkey: hex!("03ee2c3d98eb1f93c0a1aa8e5a4009b70eb7b44ead15f1666f136b012ad58d3068").to_vec(),
952+
leaf_hash: None
953+
}
954+
]
955+
});
950956
}
951957

952958
#[test]
@@ -972,7 +978,10 @@ mod tests {
972978
))
973979
.unwrap();
974980

975-
let Response::PsbtSigned(partial_signatures) = response else {
981+
let Response::PsbtSigned {
982+
signatures: partial_signatures,
983+
} = response
984+
else {
976985
panic!("Expected PsbtSigned response");
977986
};
978987

@@ -1023,7 +1032,10 @@ mod tests {
10231032
))
10241033
.unwrap();
10251034

1026-
let Response::PsbtSigned(partial_signatures) = response else {
1035+
let Response::PsbtSigned {
1036+
signatures: partial_signatures,
1037+
} = response
1038+
else {
10271039
panic!("Expected PsbtSigned response");
10281040
};
10291041

apps/bitcoin/app/src/main.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,23 @@ async fn handle_request(
7676

7777
#[sdk::handler]
7878
async fn process_message(app: &mut App, request: &[u8]) -> Vec<u8> {
79-
let Ok(request) = postcard::from_bytes(request) else {
80-
return postcard::to_allocvec(&Response::Error(common::errors::Error::InvalidRequest))
81-
.unwrap();
79+
let mut decoder = minicbor::Decoder::new(request);
80+
let Ok(request) = decoder.decode::<Request>() else {
81+
return minicbor::to_vec(&Response::Error {
82+
error: common::errors::Error::InvalidRequest,
83+
})
84+
.unwrap();
8285
};
86+
if decoder.position() != request.len() {
87+
return minicbor::to_vec(&Response::Error {
88+
error: common::errors::Error::InvalidRequest,
89+
})
90+
.unwrap();
91+
}
8392
let response = handle_request(app, &request)
8493
.await
85-
.unwrap_or_else(|e| Response::Error(e));
86-
postcard::to_allocvec(&response).unwrap()
94+
.unwrap_or_else(|error| Response::Error { error });
95+
minicbor::to_vec(&response).unwrap()
8796
}
8897

8998
pub fn main() {

apps/bitcoin/client/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ base58 = { package = "base58ck", version = "0.1", default-features = false }
2020
common = { package = "vnd-bitcoin-common", path = "../common", default-features = false, features = ["target_native"]}
2121
hex = "0.4.3"
2222
hidapi = "2.6.3"
23+
minicbor = { version = "2.2", features = ["alloc", "derive", "std"] }
2324
sdk = { package = "vanadium-client-sdk", path = "../../../client-sdk" }
24-
serde = "1.0.219"
2525
tokio = { version = "1.38.1", features = ["io-util", "macros", "net", "rt", "rt-multi-thread", "sync"] }
2626

2727
# The following dependencies are only used for the binary executable.
@@ -30,7 +30,6 @@ tokio = { version = "1.38.1", features = ["io-util", "macros", "net", "rt", "rt-
3030
clap = { version = "4.5.31", features = ["derive"] }
3131
rustyline = "15.0.0"
3232
shellwords = "1.1.0"
33-
postcard = { version = "1.1.1", features = ["alloc"] }
3433
base64 = { version = "0.22.1", default-features = false, features = ["alloc"] }
3534
env_logger = { version = "0.11.8", optional = true }
3635

apps/bitcoin/client/src/client.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,23 @@ impl<'a> BitcoinClient {
8787

8888
// Parse app response; if the response is a Response::Error, it is converted to BitcoinClientError::AppError.
8989
async fn parse_response(response_raw: &'a [u8]) -> Result<Response, BitcoinClientError> {
90-
let resp: Response = postcard::from_bytes(response_raw).map_err(|_| {
90+
let mut decoder = minicbor::Decoder::new(response_raw);
91+
let resp: Response = decoder.decode().map_err(|_| {
9192
BitcoinClientError::GenericError("Failed to parse response".to_string())
9293
})?;
93-
if let Response::Error(e) = resp {
94-
return Err(BitcoinClientError::AppError(e));
94+
if decoder.position() != response_raw.len() {
95+
return Err(BitcoinClientError::GenericError(
96+
"Failed to parse response".to_string(),
97+
));
98+
}
99+
if let Response::Error { error } = resp {
100+
return Err(BitcoinClientError::AppError(error));
95101
}
96102
Ok(resp)
97103
}
98104

99105
pub async fn exit(&mut self) -> Result<i32, BitcoinClientError> {
100-
let msg = postcard::to_allocvec(&Request::Exit).map_err(|_| {
106+
let msg = minicbor::to_vec(&Request::Exit).map_err(|_| {
101107
BitcoinClientError::GenericError("Failed to serialize Exit request".to_string())
102108
})?;
103109

@@ -123,15 +129,15 @@ impl<'a> BitcoinClient {
123129
&mut self,
124130
tree: message::KeyTree,
125131
) -> Result<u32, BitcoinClientError> {
126-
let msg = postcard::to_allocvec(&Request::GetMasterFingerprint { tree }).map_err(|_| {
132+
let msg = minicbor::to_vec(&Request::GetMasterFingerprint { tree }).map_err(|_| {
127133
BitcoinClientError::GenericError(
128134
"Failed to serialize GetMasterFingerprint request".to_string(),
129135
)
130136
})?;
131137

132138
let response_raw = self.send_message(&msg).await?;
133139
match Self::parse_response(&response_raw).await? {
134-
Response::MasterFingerprint(fpr) => Ok(fpr),
140+
Response::MasterFingerprint { fingerprint } => Ok(fingerprint),
135141
e => Err(BitcoinClientError::InvalidResponse(format!(
136142
"Invalid response: {:?}",
137143
e
@@ -149,7 +155,7 @@ impl<'a> BitcoinClient {
149155
let path = DerivationPath::from_str(bip32_path)
150156
.map_err(|e| format!("Failed to convert bip32_path: {}", e))?;
151157

152-
let msg = postcard::to_allocvec(&Request::GetExtendedPubkey {
158+
let msg = minicbor::to_vec(&Request::GetExtendedPubkey {
153159
tree,
154160
display,
155161
path: message::Bip32Path(path.to_u32_vec()),
@@ -182,7 +188,7 @@ impl<'a> BitcoinClient {
182188
display: bool,
183189
) -> Result<[u8; 78], BitcoinClientError> {
184190
let path = common::identity::identity_derivation_path(index);
185-
let msg = postcard::to_allocvec(&Request::GetExtendedPubkey {
191+
let msg = minicbor::to_vec(&Request::GetExtendedPubkey {
186192
tree: message::KeyTree::Standard,
187193
display,
188194
path: message::Bip32Path(path),
@@ -222,7 +228,7 @@ impl<'a> BitcoinClient {
222228
),
223229
BitcoinClientError,
224230
> {
225-
let msg = postcard::to_allocvec(&Request::RegisterAccount {
231+
let msg = minicbor::to_vec(&Request::RegisterAccount {
226232
name: name.into(),
227233
account: account.clone(),
228234
registered_identities,
@@ -259,7 +265,7 @@ impl<'a> BitcoinClient {
259265
),
260266
BitcoinClientError,
261267
> {
262-
let msg = postcard::to_allocvec(&Request::RegisterIdentityKey {
268+
let msg = minicbor::to_vec(&Request::RegisterIdentityKey {
263269
name: name.into(),
264270
pubkey: pubkey.to_vec(),
265271
})
@@ -291,7 +297,7 @@ impl<'a> BitcoinClient {
291297
display: bool,
292298
identity_index: Option<u32>,
293299
) -> Result<(String, Option<IdentitySignature>), BitcoinClientError> {
294-
let msg = postcard::to_allocvec(&Request::GetAddress {
300+
let msg = minicbor::to_vec(&Request::GetAddress {
295301
display,
296302
name: Some(name.to_string()),
297303
account: account.clone(),
@@ -322,7 +328,7 @@ impl<'a> BitcoinClient {
322328
&mut self,
323329
psbt: &[u8],
324330
) -> Result<Vec<PartialSignature>, BitcoinClientError> {
325-
let msg = postcard::to_allocvec(&Request::SignPsbt {
331+
let msg = minicbor::to_vec(&Request::SignPsbt {
326332
psbt: psbt.to_vec(),
327333
})
328334
.map_err(|_| {
@@ -331,7 +337,7 @@ impl<'a> BitcoinClient {
331337

332338
let response_raw = self.send_message(&msg).await?;
333339
match Self::parse_response(&response_raw).await? {
334-
Response::PsbtSigned(partial_sigs) => Ok(partial_sigs),
340+
Response::PsbtSigned { signatures } => Ok(signatures),
335341
e => Err(BitcoinClientError::InvalidResponse(format!(
336342
"Invalid response: {:?}",
337343
e

apps/bitcoin/common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ base58 = { package = "base58ck", version = "0.1", default-features = false }
2121

2222
base64 = { version = "0.22.1", default-features = false, features = ["alloc"] }
2323
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
24+
minicbor = { version = "2.2", default-features = false, features = ["alloc", "derive"] }
2425
sdk = { package = "vanadium-app-sdk", path = "../../../app-sdk", default-features = false }
25-
serde = { version = "1.0.219", default-features = false, features = ["alloc"] }
2626
subtle = { version="2.6.1", default-features = false }
2727

2828
[dev-dependencies]

0 commit comments

Comments
 (0)