Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a convert tool between bech32 and hex #1502

Open
wants to merge 16 commits into
base: shimmer-develop
Choose a base branch
from
Open
151 changes: 151 additions & 0 deletions bee-node/bee-node/src/tools/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use bee_block::address::{Address, Ed25519Address};
use crypto::hashes::{blake2b::Blake2b256, Digest};
use structopt::StructOpt;
use thiserror::Error;

#[derive(Clone, Debug, Error)]
pub enum ConvertError {
#[error("invalid Bech32 address length")]
InvalidAddressLength(),
#[error("Invalid Address")]
InvalidAddress(),
}

#[derive(Clone, Debug, StructOpt)]
pub enum ConvertTool {
/// Converts a Bech32 address to a hex encoded one.
Bech32ToHex {
#[structopt(long)]
bech32: String,
},
/// Converts a hex encoded address to a Bech32 one.
HexToBech32 {
#[structopt(long)]
hex: String,
#[structopt(long)]
hrp: String,
},
/// Converts a hex encoded public key to a Bech32 address.
HexPubkeyToBech32 {
#[structopt(long)]
pubkey: String,
#[structopt(long)]
hrp: String,
},
}

pub fn exec(tool: &ConvertTool) -> Result<(), ConvertError> {
match tool {
ConvertTool::Bech32ToHex { bech32 } => {
let hex = bech32_to_hex(bech32.as_str());
match hex {
Ok(_) => println!("Your Hex encoded address is:\t{}", hex.unwrap()),
Err(e) => println!("Error: {}", e),
}
}
ConvertTool::HexToBech32 { hex, hrp } => {
let bech32 = hex_to_bech32(hex.as_str(), hrp.as_str());
match bech32 {
Ok(_) => println!("Your Bech32 address is:\t{:?}", bech32.unwrap()),
Err(e) => println!("Error: {}", e),
}
}
ConvertTool::HexPubkeyToBech32 { pubkey, hrp } => {
let bech32 = hex_public_key_to_bech32_address(pubkey.as_str(), hrp.as_str());
match bech32 {
Ok(_) => println!("Your Bech32 address is:\t{:?}", bech32.unwrap()),
Err(e) => println!("Error: {}", e),
}
}
}
Ok(())
}

/// Transforms bech32 to hex
fn bech32_to_hex(bech32: &str) -> Result<String, ConvertError> {
let address = Address::try_from_bech32(bech32).map_err(|_| ConvertError::InvalidAddress())?;
let hex_string = match address {
(_, Address::Ed25519(ed)) => ed.to_string(),
(_, Address::Alias(alias)) => alias.to_string(),
(_, Address::Nft(nft)) => nft.to_string(),
};
Ok(hex_string)
}

/// Transforms a hex encoded address to a bech32 encoded address
fn hex_to_bech32(hex: &str, bech32_hrp: &str) -> Result<String, ConvertError> {
let address: Ed25519Address = hex
.parse::<Ed25519Address>()
.map_err(|_| ConvertError::InvalidAddress())?;
Ok(Address::Ed25519(address).to_bech32(bech32_hrp))
}

/// Transforms a hex encoded public key to a bech32 encoded address
fn hex_public_key_to_bech32_address(hex: &str, bech32_hrp: &str) -> Result<String, ConvertError> {
let mut public_key = [0u8; Ed25519Address::LENGTH];
hex::decode_to_slice(&hex, &mut public_key).map_err(|_| ConvertError::InvalidAddressLength())?;
hackphobic marked this conversation as resolved.
Show resolved Hide resolved

let address = Blake2b256::digest(&public_key)
.try_into()
.map_err(|_e| ConvertError::InvalidAddress())?;
let address: Ed25519Address = Ed25519Address::new(address);
Ok(Address::Ed25519(address).to_bech32(bech32_hrp))
}

#[cfg(test)]
mod bech32tests {
use crate::tools::convert::*;
// spec: https://github.com/iotaledger/tips/blob/main/tips/TIP-0011/tip-0011.md
const HRP: &str = "iota";

#[test]
fn test_bech32_to_hex() {
let bech32_address = "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx".to_string();
let hex_encoded_address = bech32_to_hex(&bech32_address).unwrap();

assert_eq!(
&hex_encoded_address,
"0xefdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3"
);

let bech32_to_hex = ConvertTool::Bech32ToHex { bech32: bech32_address };
exec(&bech32_to_hex).unwrap();
}

#[test]
fn test_hex_to_bech32() {
let hex_address = "0xefdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3".to_string();
let bech32_address = hex_to_bech32(&hex_address, HRP).unwrap();

assert_eq!(
&bech32_address,
"iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx"
);

let hex_to_bech32 = ConvertTool::HexToBech32 {
hex: hex_address,
hrp: HRP.to_string(),
};
exec(&hex_to_bech32).unwrap();
}

#[test]
fn test_public_key_to_bech32() {
let public_key = "6f1581709bb7b1ef030d210db18e3b0ba1c776fba65d8cdaad05415142d189f8".to_string();
let bech32_address = hex_public_key_to_bech32_address(&public_key, HRP).unwrap();

assert_eq!(
&bech32_address,
"iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx"
);

let public_key_to_bech32 = ConvertTool::HexPubkeyToBech32 {
pubkey: public_key,
hrp: HRP.to_string(),
};
exec(&public_key_to_bech32).unwrap();
}
}
6 changes: 6 additions & 0 deletions bee-node/bee-node/src/tools/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

mod convert;
mod ed25519;
mod jwt_api;
mod password;
Expand Down Expand Up @@ -32,6 +33,8 @@ pub enum Tool {
Password(password::PasswordTool),
/// Generates a JWT for the Node API.
JwtApi(jwt_api::JwtApiTool),
/// Converts back & forth between Bech32 and Hex.
Convert(convert::ConvertTool),
}

#[derive(Debug, Error)]
Expand All @@ -50,6 +53,8 @@ pub enum ToolError {
Password(#[from] password::PasswordError),
#[error("{0}")]
JwtApi(#[from] jwt_api::JwtApiError),
#[error("{0}")]
Convert(#[from] convert::ConvertError),
}

pub fn exec<B: NodeStorageBackend>(tool: &Tool, local: &Local, node_config: &NodeConfig<B>) -> Result<(), ToolError> {
Expand All @@ -62,6 +67,7 @@ pub fn exec<B: NodeStorageBackend>(tool: &Tool, local: &Local, node_config: &Nod
Tool::SnapshotInfo(tool) => snapshot_info::exec(tool)?,
Tool::Password(tool) => password::exec(tool)?,
Tool::JwtApi(tool) => jwt_api::exec(tool, local, node_config)?,
Tool::Convert(tool) => convert::exec(tool)?,
}

Ok(())
Expand Down