docs: better info about writing types containing data
This commit is contained in:
parent
bf50e2aeac
commit
2e3c387ae1
|
@ -78,8 +78,8 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
#[cfg(feature = "qrcode")]
|
||||
{
|
||||
use keyfork_qrcode::{qrencode, ErrorCorrection};
|
||||
let mut qrcode_data = nonce_mnemonic.entropy();
|
||||
qrcode_data.extend(key_mnemonic.entropy());
|
||||
let mut qrcode_data = nonce_mnemonic.to_bytes();
|
||||
qrcode_data.extend(key_mnemonic.as_bytes());
|
||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Medium) {
|
||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||
}
|
||||
|
@ -120,10 +120,10 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
let [pubkey_mnemonic, payload_mnemonic] =
|
||||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||
let pubkey = pubkey_mnemonic
|
||||
.entropy()
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| InvalidData)?;
|
||||
let payload = payload_mnemonic.entropy();
|
||||
let payload = payload_mnemonic.to_bytes();
|
||||
(pubkey, payload)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -495,11 +495,11 @@ pub fn decrypt(
|
|||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||
|
||||
let nonce = nonce_mnemonic
|
||||
.entropy()
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| InvalidData)?;
|
||||
let pubkey = pubkey_mnemonic
|
||||
.entropy()
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| InvalidData)?;
|
||||
(nonce, pubkey)
|
||||
|
@ -560,8 +560,8 @@ pub fn decrypt(
|
|||
#[cfg(feature = "qrcode")]
|
||||
{
|
||||
use keyfork_qrcode::{qrencode, ErrorCorrection};
|
||||
let mut qrcode_data = our_pubkey_mnemonic.entropy();
|
||||
qrcode_data.extend(payload_mnemonic.entropy());
|
||||
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
||||
qrcode_data.extend(payload_mnemonic.as_bytes());
|
||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Lowest) {
|
||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ impl RecoverSubcommands {
|
|||
3,
|
||||
validator.to_fn(),
|
||||
)?;
|
||||
Ok(mnemonic.entropy())
|
||||
Ok(mnemonic.to_bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,17 +265,27 @@ impl Mnemonic {
|
|||
&self.entropy
|
||||
}
|
||||
|
||||
/// The internal representation of the decoded data, as a [`Vec<u8>`].
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.entropy.to_vec()
|
||||
}
|
||||
|
||||
/// Drop self, returning the decoded data.
|
||||
pub fn to_bytes(self) -> Vec<u8> {
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.entropy
|
||||
}
|
||||
|
||||
/// Clone the existing entropy.
|
||||
#[deprecated]
|
||||
pub fn entropy(&self) -> Vec<u8> {
|
||||
self.entropy.clone()
|
||||
}
|
||||
|
||||
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
||||
///
|
||||
/// # Errors
|
||||
/// The method may return an error if the pbkdf2 function returns an invalid length, but this
|
||||
/// case should not be reached.
|
||||
pub fn seed<'a>(
|
||||
&self,
|
||||
passphrase: impl Into<Option<&'a str>>,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
# Developers Guide
|
||||
|
||||
- [Handling Data](./dev-guide/handling-data.md)
|
||||
- [Writing Binaries](./dev-guide/index.md)
|
||||
- [Provisioners](./dev-guide/provisioners.md)
|
||||
- [Auditing Dependencies](./dev-guide/auditing.md)
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
# Handling Data
|
||||
|
||||
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
|
||||
methods perform three different, but related, functions, and it's important to
|
||||
choose the correct name when writing methods. These methods take data that are
|
||||
stored inside of the struct, and return them in some manner. For example, let's
|
||||
look at the `Mnemonic` struct:
|
||||
|
||||
```rust
|
||||
pub struct Mnemonic {
|
||||
entropy: Vec<u8>,
|
||||
// fields omitted
|
||||
}
|
||||
```
|
||||
|
||||
We're going to define the three methods `as_bytes()`, `to_bytes()`, and
|
||||
`into_bytes()`, explaining what the details of each are. To start with:
|
||||
|
||||
### `Mnemonic::as_bytes()`
|
||||
|
||||
This method will return a `&[u8]`, a reference to a slice of bytes, without
|
||||
allocating anything. The `Mnemonic` type is not consumed by this process.
|
||||
|
||||
```rust
|
||||
impl Mnemonic {
|
||||
/// Return a slice of bytes represented by the mnemonic.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.entropy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `Mnemonic::to_bytes()`
|
||||
|
||||
This method will return a `Vec<u8>`, an allocated type containing the bytes,
|
||||
without consuming the `Mnemonic` type. This is useful if you may need the
|
||||
`Mnemonic` type in the future.
|
||||
|
||||
```rust
|
||||
impl Mnemonic {
|
||||
/// Return a `Vec<u8>` of bytes represented by the mnemonic.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.entropy.to_vec()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `Mnemonic::into_bytes()`
|
||||
|
||||
This method will return the internal representation of bytes. No allocation is
|
||||
performed, but the `Mnemonic` value is no longer accessible.
|
||||
|
||||
```rust
|
||||
impl Mnemonic {
|
||||
/// Return a Vec<u8> of bytes represented by the mnemonic.
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.entropy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Data: `Mnemonic::seed()`
|
||||
|
||||
Sometimes you may have an operation (such as creating a seed from a mnemonic)
|
||||
which can't possibly happen without allocating some kind of data. In that case,
|
||||
it is not reasonable - or even possible - to have three methods for that case.
|
||||
In that case, it is reasonable to have a method with a "bare" name, such as
|
||||
`Mnemonic::seed()`. Additionally, unless it makes sense to make the type
|
||||
unusable after such an operation (using the type system to your advantage), it
|
||||
makes sense to borrow from self while returning the allocated value.
|
||||
|
||||
```rust
|
||||
impl Mnemonic {
|
||||
/// Create a seed from the mnemonic's data.
|
||||
pub fn seed(&self) -> Result<Vec<u8>, Error> {
|
||||
let result = perform_secret_operation(self.entropy)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Complex Example: `Mnemonic::into_parts()`
|
||||
|
||||
Let's add a `Wordlist` to our `Mnemonic` type, so we can format it as a string.
|
||||
|
||||
```rust
|
||||
pub struct Mnemonic {
|
||||
entropy: Vec<u8>,
|
||||
wordlist: Wordlist,
|
||||
}
|
||||
```
|
||||
|
||||
Because we now have two expensive-to-allocate variables in our type, we need to
|
||||
change how we deconstruct it into parts:
|
||||
|
||||
```rust
|
||||
impl Mnemonic {
|
||||
/// Split the Mnemonic into its interior parts.
|
||||
pub fn into_parts(self) -> (Vec<u8>, Wordlist) {
|
||||
let Mnemonic { entropy, wordlist } = self;
|
||||
(entropy, wordlist)
|
||||
}
|
||||
|
||||
/// Return a Vec<u8> of bytes represented by the mnemonic.
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.into_parts().0
|
||||
}
|
||||
|
||||
/// Return the Wordlist used by the mnemonic.
|
||||
pub fn into_wordlist(self) -> Wordlist {
|
||||
self.into_parts().1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In practical use, `Mnemonic` stores `wordlist` as an `Arc<T>` to allow reusing
|
||||
the same wordlist (parsing it is expensive!), but we'll ignore that for
|
||||
demonstration purposes.
|
Loading…
Reference in New Issue