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
105 changes: 105 additions & 0 deletions bee-node/bee-node/src/tools/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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;

const BECH32_HRP: &str = "iota";
hackphobic marked this conversation as resolved.
Show resolved Hide resolved

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

#[derive(Clone, Debug, StructOpt)]
pub enum ConvertTool {
/// convert Bech32 address to hex encoding
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
Bech32ToHex { bech32: String },
/// convert hex encoding to Bech32 address
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
HexToBech32 { hex: String },
/// convert a hex encoded public key to Bech32 address
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
HexPubkeyToBech32 { pubkey: String },
}

pub fn exec(tool: &ConvertTool) -> Result<(), ConvertError> {
match tool {
ConvertTool::Bech32ToHex { bech32 } => {
let hex = bech32_to_hex(bech32.as_str()).unwrap();
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
println!("Your Hex encoded address is:\t{:?}", hex);
}
ConvertTool::HexToBech32 { hex } => {
let bech32 = hex_to_bech32(hex.as_str(), BECH32_HRP).unwrap();
println!("Your Bech32 address is:\t{:?}", bech32);
}
ConvertTool::HexPubkeyToBech32 { pubkey } => {
let bech32 = hex_public_key_to_bech32_address(pubkey.as_str(), BECH32_HRP).unwrap();
println!("Your Bech32 address is:\t{:?}", bech32);
}
}
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
#[test]
fn bech32tohex() {
let bech32tohex = ConvertTool::Bech32ToHex {
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
bech32: "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx".to_string(),
};
exec(&bech32tohex).unwrap(); // output: "0xefdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3"
}

#[test]
fn hextobech32() {
let hextobech32 = ConvertTool::HexToBech32 {
hex: "0xefdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3".to_string(),
};
exec(&hextobech32).unwrap(); // output: "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx"
}

#[test]
fn pubkeytohex() {
let pubkeytohex = ConvertTool::HexPubkeyToBech32 {
pubkey: "6f1581709bb7b1ef030d210db18e3b0ba1c776fba65d8cdaad05415142d189f8".to_string(),
hackphobic marked this conversation as resolved.
Show resolved Hide resolved
};
exec(&pubkeytohex).unwrap(); // output: "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx"
}
}
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