Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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())?;

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