Addresses

Tofuri addresses consist of a total of 50 characters, which can be broken down as follows:

  • The prefix 0x takes up 2 characters.
  • The raw address, which is 20 bytes long, is hex encoded and takes up 40 characters.
  • The checksum, which is 4 bytes long, is hex encoded and takes up 8 characters.

Filename: tofuri/address/src/lib.rs

use sha2::Digest;
use sha2::Sha256;
use tofuri_core::*;
#[derive(Debug)]
pub enum Error {
    Hex(hex::FromHexError),
    InvalidAddress,
    InvalidAddressChecksum,
    AddressChecksumMismatch,
    InvalidSecretKey,
    InvalidSecretKeyChecksum,
    SecretKeyChecksumMismatch,
}
pub fn checksum(bytes: &[u8]) -> [u8; 4] {
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    let hash = hasher.finalize();
    let mut checksum = [0; 4];
    checksum.copy_from_slice(&hash[..4]);
    checksum
}
pub mod address {
    use super::*;
    pub fn encode(address: &AddressBytes) -> String {
        [
            PREFIX_ADDRESS,
            &hex::encode(address),
            &hex::encode(checksum(address)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<AddressBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_ADDRESS, "", 1)).map_err(Error::Hex)?;
        let address_bytes: AddressBytes = decoded
            .get(0..20)
            .ok_or(Error::InvalidAddress)?
            .try_into()
            .unwrap();
        if checksum(&address_bytes) == decoded.get(20..).ok_or(Error::InvalidAddressChecksum)? {
            Ok(address_bytes)
        } else {
            Err(Error::AddressChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                "0x0000000000000000000000000000000000000000de47c9b2",
                encode(&[0; 20])
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                [0; 20],
                decode("0x0000000000000000000000000000000000000000de47c9b2").unwrap()
            );
        }
    }
}
pub mod secret {
    use super::*;
    pub fn encode(secret_key: &SecretKeyBytes) -> String {
        [
            PREFIX_SECRET_KEY,
            &hex::encode(secret_key),
            &hex::encode(checksum(secret_key)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<SecretKeyBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_SECRET_KEY, "", 1)).map_err(Error::Hex)?;
        let secret_key_bytes: SecretKeyBytes = decoded
            .get(0..32)
            .ok_or(Error::InvalidSecretKey)?
            .try_into()
            .unwrap();
        if checksum(&secret_key_bytes)
            == decoded.get(32..).ok_or(Error::InvalidSecretKeyChecksum)?
        {
            Ok(secret_key_bytes)
        } else {
            Err(Error::SecretKeyChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                encode(&[0; 32]),
                "SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad"
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                decode("SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad").unwrap(),
                [0; 32]
            );
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_cecksum() {
        assert_eq!(checksum(&[0; 32]), [102, 104, 122, 173]);
        assert_eq!(checksum(&[0; 33]), [127, 156, 158, 49]);
    }
}

Checksum

Tofuri addresses are designed to mitigate the risks of typographical errors that can lead to the loss of funds. To achieve this, a hash checksum of 4 bytes is added to the end of each address. This additional layer of security ensures that even if an incorrect address is mistakenly entered, the checksum validation process will prevent it from being accepted as a valid address. As a result, the probability of an incorrect address being accepted is greatly reduced, with a probability of 1 in 4,294,967,296.

By including a checksum, Tofuri addresses help prevent the accidental sending of funds to non-existent or incorrect addresses due to typos or other errors. This enhances the overall security and reliability of the system, ensuring that transactions are executed smoothly and without any mishaps.

If you ever happen to misstype an address and still have the checksum accept it as valid, you can either feel lucky, or start questioning the randomness of the universe!

Filename: tofuri/address/src/lib.rs

use sha2::Digest;
use sha2::Sha256;
use tofuri_core::*;
#[derive(Debug)]
pub enum Error {
    Hex(hex::FromHexError),
    InvalidAddress,
    InvalidAddressChecksum,
    AddressChecksumMismatch,
    InvalidSecretKey,
    InvalidSecretKeyChecksum,
    SecretKeyChecksumMismatch,
}
pub fn checksum(bytes: &[u8]) -> [u8; 4] {
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    let hash = hasher.finalize();
    let mut checksum = [0; 4];
    checksum.copy_from_slice(&hash[..4]);
    checksum
}
pub mod address {
    use super::*;
    pub fn encode(address: &AddressBytes) -> String {
        [
            PREFIX_ADDRESS,
            &hex::encode(address),
            &hex::encode(checksum(address)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<AddressBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_ADDRESS, "", 1)).map_err(Error::Hex)?;
        let address_bytes: AddressBytes = decoded
            .get(0..20)
            .ok_or(Error::InvalidAddress)?
            .try_into()
            .unwrap();
        if checksum(&address_bytes) == decoded.get(20..).ok_or(Error::InvalidAddressChecksum)? {
            Ok(address_bytes)
        } else {
            Err(Error::AddressChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                "0x0000000000000000000000000000000000000000de47c9b2",
                encode(&[0; 20])
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                [0; 20],
                decode("0x0000000000000000000000000000000000000000de47c9b2").unwrap()
            );
        }
    }
}
pub mod secret {
    use super::*;
    pub fn encode(secret_key: &SecretKeyBytes) -> String {
        [
            PREFIX_SECRET_KEY,
            &hex::encode(secret_key),
            &hex::encode(checksum(secret_key)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<SecretKeyBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_SECRET_KEY, "", 1)).map_err(Error::Hex)?;
        let secret_key_bytes: SecretKeyBytes = decoded
            .get(0..32)
            .ok_or(Error::InvalidSecretKey)?
            .try_into()
            .unwrap();
        if checksum(&secret_key_bytes)
            == decoded.get(32..).ok_or(Error::InvalidSecretKeyChecksum)?
        {
            Ok(secret_key_bytes)
        } else {
            Err(Error::SecretKeyChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                encode(&[0; 32]),
                "SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad"
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                decode("SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad").unwrap(),
                [0; 32]
            );
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_cecksum() {
        assert_eq!(checksum(&[0; 32]), [102, 104, 122, 173]);
        assert_eq!(checksum(&[0; 33]), [127, 156, 158, 49]);
    }
}

Secret keys

Secret keys are encoded with the prefix SECRETx and are longer than Tofuri addresses. A Tofuri secret key consists of a total of 79 characters, which can be broken down as follows:

  • The prefix SECRETx takes up 7 characters.
  • The secret key, which is 32 bytes long, is hex encoded and takes up 64 characters.
  • The checksum, which is 4 bytes long, is hex encoded and takes up 8 characters.

It's also important to remember that if a secret key is lost or stolen, it can result in the permanent loss of associated funds. Therefore, it's crucial to keep secret keys safe and secure.

Filename: tofuri/address/src/lib.rs

use sha2::Digest;
use sha2::Sha256;
use tofuri_core::*;
#[derive(Debug)]
pub enum Error {
    Hex(hex::FromHexError),
    InvalidAddress,
    InvalidAddressChecksum,
    AddressChecksumMismatch,
    InvalidSecretKey,
    InvalidSecretKeyChecksum,
    SecretKeyChecksumMismatch,
}
pub fn checksum(bytes: &[u8]) -> [u8; 4] {
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    let hash = hasher.finalize();
    let mut checksum = [0; 4];
    checksum.copy_from_slice(&hash[..4]);
    checksum
}
pub mod address {
    use super::*;
    pub fn encode(address: &AddressBytes) -> String {
        [
            PREFIX_ADDRESS,
            &hex::encode(address),
            &hex::encode(checksum(address)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<AddressBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_ADDRESS, "", 1)).map_err(Error::Hex)?;
        let address_bytes: AddressBytes = decoded
            .get(0..20)
            .ok_or(Error::InvalidAddress)?
            .try_into()
            .unwrap();
        if checksum(&address_bytes) == decoded.get(20..).ok_or(Error::InvalidAddressChecksum)? {
            Ok(address_bytes)
        } else {
            Err(Error::AddressChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                "0x0000000000000000000000000000000000000000de47c9b2",
                encode(&[0; 20])
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                [0; 20],
                decode("0x0000000000000000000000000000000000000000de47c9b2").unwrap()
            );
        }
    }
}
pub mod secret {
    use super::*;
    pub fn encode(secret_key: &SecretKeyBytes) -> String {
        [
            PREFIX_SECRET_KEY,
            &hex::encode(secret_key),
            &hex::encode(checksum(secret_key)),
        ]
        .concat()
    }
    pub fn decode(str: &str) -> Result<SecretKeyBytes, Error> {
        let decoded = hex::decode(str.replacen(PREFIX_SECRET_KEY, "", 1)).map_err(Error::Hex)?;
        let secret_key_bytes: SecretKeyBytes = decoded
            .get(0..32)
            .ok_or(Error::InvalidSecretKey)?
            .try_into()
            .unwrap();
        if checksum(&secret_key_bytes)
            == decoded.get(32..).ok_or(Error::InvalidSecretKeyChecksum)?
        {
            Ok(secret_key_bytes)
        } else {
            Err(Error::SecretKeyChecksumMismatch)
        }
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_encode() {
            assert_eq!(
                encode(&[0; 32]),
                "SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad"
            );
        }
        #[test]
        fn test_decode() {
            assert_eq!(
                decode("SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad").unwrap(),
                [0; 32]
            );
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_cecksum() {
        assert_eq!(checksum(&[0; 32]), [102, 104, 122, 173]);
        assert_eq!(checksum(&[0; 33]), [127, 156, 158, 49]);
    }
}