Add util functions for bytes32 string encoding/decoding (#337)

This commit is contained in:
Jonathan LEI 2021-07-12 16:20:38 +08:00 committed by GitHub
parent 330b62c986
commit 4afa1c9517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 1 deletions

7
Cargo.lock generated
View File

@ -808,6 +808,7 @@ dependencies = [
"generic-array 0.14.4", "generic-array 0.14.4",
"glob", "glob",
"hex", "hex",
"hex-literal",
"k256", "k256",
"once_cell", "once_cell",
"rand 0.8.4", "rand 0.8.4",
@ -1174,6 +1175,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76505e26b6ca3bbdbbb360b68472abbb80998c5fa5dc43672eca34f28258e138"
[[package]] [[package]]
name = "hidapi" name = "hidapi"
version = "1.2.6" version = "1.2.6"

View File

@ -44,7 +44,7 @@ ethers = { version = "0.4.0", path = "../ethers" }
serde_json = { version = "1.0.64", default-features = false } serde_json = { version = "1.0.64", default-features = false }
bincode = { version = "1.3.3", default-features = false } bincode = { version = "1.3.3", default-features = false }
once_cell = { version = "1.8.0" } once_cell = { version = "1.8.0" }
hex-literal = "0.3.2"
[features] [features]
celo = [] # celo support extends the transaction format with extra fields celo = [] # celo support extends the transaction format with extra fields

View File

@ -36,6 +36,13 @@ pub use rlp;
use crate::types::{Address, Bytes, U256}; use crate::types::{Address, Bytes, U256};
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey}; use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
use std::convert::TryInto; use std::convert::TryInto;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum FormatBytes32StringError {
#[error("bytes32 strings must not exceed 32 bytes in length")]
TextTooLong,
}
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei /// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
pub const WEI_IN_ETHER: U256 = U256([0x0de0b6b3a7640000, 0x0, 0x0, 0x0]); pub const WEI_IN_ETHER: U256 = U256([0x0de0b6b3a7640000, 0x0, 0x0, 0x0]);
@ -156,6 +163,30 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
}) })
} }
/// Returns a bytes32 string representation of text. If the length of text exceeds 32 bytes,
/// an error is returned.
pub fn format_bytes32_string(text: &str) -> Result<[u8; 32], FormatBytes32StringError> {
let str_bytes: &[u8] = text.as_bytes();
if str_bytes.len() > 32 {
return Err(FormatBytes32StringError::TextTooLong);
}
let mut bytes32: [u8; 32] = [0u8; 32];
bytes32[..str_bytes.len()].copy_from_slice(str_bytes);
Ok(bytes32)
}
/// Returns the decoded string represented by the bytes32 encoded data.
pub fn parse_bytes32_string(bytes: &[u8; 32]) -> Result<&str, std::str::Utf8Error> {
let mut length = 0;
while length < 32 && bytes[length] != 0 {
length += 1;
}
std::str::from_utf8(&bytes[..length])
}
/// A bit of hack to find an unused TCP port. /// A bit of hack to find an unused TCP port.
/// ///
/// Does not guarantee that the given port is unused after the function exists, just that it was /// Does not guarantee that the given port is unused after the function exists, just that it was
@ -173,6 +204,7 @@ pub(crate) fn unused_port() -> u16 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use hex_literal::hex;
#[test] #[test]
fn wei_in_ether() { fn wei_in_ether() {
@ -359,4 +391,64 @@ mod tests {
assert_eq!(expected, get_create2_address(from, salt, init_code)) assert_eq!(expected, get_create2_address(from, salt, init_code))
} }
} }
#[test]
fn bytes32_string_parsing() {
let text_bytes_list = vec![
(
"",
hex!("0000000000000000000000000000000000000000000000000000000000000000"),
),
(
"A",
hex!("4100000000000000000000000000000000000000000000000000000000000000"),
),
(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
),
(
"!@#$%^&*(),./;'[]",
hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
),
];
for (text, bytes) in text_bytes_list {
assert_eq!(text, parse_bytes32_string(&bytes).unwrap());
}
}
#[test]
fn bytes32_string_formatting() {
let text_bytes_list = vec![
(
"",
hex!("0000000000000000000000000000000000000000000000000000000000000000"),
),
(
"A",
hex!("4100000000000000000000000000000000000000000000000000000000000000"),
),
(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
),
(
"!@#$%^&*(),./;'[]",
hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"),
),
];
for (text, bytes) in text_bytes_list {
assert_eq!(bytes, format_bytes32_string(text).unwrap());
}
}
#[test]
fn bytes32_string_formatting_too_long() {
assert!(matches!(
format_bytes32_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(),
FormatBytes32StringError::TextTooLong
));
}
} }