The crypto module provides Ed25519 cryptographic primitives for digital signatures and address derivation. It includes types for addresses, signatures, public keys, and keypairs.
Overview
This module uses the Ed25519 signature scheme for transaction signing and verification. Addresses are derived from public keys using Blake3 hashing.
Types
AddressBytes
pub type AddressBytes = [u8; 20];
A type alias for a 20-byte array representing an address.
Address
pub struct Address(pub AddressBytes);
An address on the blockchain, derived from the first 20 bytes of a public key hash.
Constants
The zero address (all zeros).pub const ZERO: Self = Self([0u8; 20]);
Methods
from_bytes
fn(bytes: AddressBytes) -> Self
Creates an address from raw bytes.let bytes: [u8; 20] = [1; 20];
let address = Address::from_bytes(bytes);
as_bytes
fn(&self) -> &AddressBytes
Returns a reference to the underlying bytes.let address = Address::ZERO;
let bytes: &[u8; 20] = address.as_bytes();
Converts the address to a hexadecimal string with “0x” prefix.let address = keypair.address();
let hex = address.to_hex();
// Returns: "0xf4a5e8c2b9d7f3a1e6c5b8d2f9a4e7c3b1d5f8a2"
from_hex
fn(s: &str) -> Result<Self, CryptoError>
Parses an address from a hexadecimal string (with or without “0x” prefix).let address = Address::from_hex("0xf4a5e8c2b9d7f3a1e6c5b8d2f9a4e7c3b1d5f8a2")?;
// Also works without prefix:
let address = Address::from_hex("f4a5e8c2b9d7f3a1e6c5b8d2f9a4e7c3b1d5f8a2")?;
Hexadecimal string (40 characters, optionally prefixed with “0x”)
Trait Implementations
- Clone, Copy, PartialEq, Eq, Hash, Default: Standard derivations
- Serialize, Deserialize: Serde support
- Display: Formats as “0x” followed by hex
- Debug: Formats as “Address(0x…)”
- AsRef: Allows using Address as a byte slice
Signature
pub struct Signature(pub [u8; 64]);
A cryptographic Ed25519 signature (64 bytes).
Methods
from_bytes
fn(bytes: [u8; 64]) -> Self
Creates a signature from raw bytes.
Returns a reference to the underlying bytes.
Converts the signature to a hexadecimal string.
Trait Implementations
- Clone, Copy, PartialEq, Eq, Default: Standard derivations
- Serialize, Deserialize: Custom serde implementation
- Display: Formats as “0x” followed by hex
- Debug: Formats as “Signature(…)” with first 16 hex characters
PublicKey
pub struct PublicKey(pub VerifyingKey);
A public key for signature verification (wraps ed25519_dalek::VerifyingKey).
Methods
Derives the address from this public key.The address is the first 20 bytes of the Blake3 hash of the public key.let keypair = Keypair::generate();
let address = keypair.public_key.to_address();
Returns the raw bytes of the public key.
verify
fn(&self, message: &[u8], signature: &Signature) -> Result<(), CryptoError>
Verifies a signature against this public key.let keypair = Keypair::generate();
let message = b"hello world";
let signature = keypair.sign(message);
keypair.public_key.verify(message, &signature)?;
The message that was signed
Trait Implementations
- Clone, PartialEq, Eq: Standard derivations
- Serialize, Deserialize: Custom serde implementation
- Debug: Formats with first 8 bytes of key
Keypair
pub struct Keypair {
signing_key: SigningKey,
pub public_key: PublicKey,
}
A keypair for signing and verification, containing both private and public keys.
Methods
Generates a new random keypair using a cryptographically secure random number generator.use minichain_core::crypto::Keypair;
let keypair = Keypair::generate();
println!("Address: {}", keypair.address());
from_private_key
fn(bytes: &[u8; 32]) -> Result<Self, CryptoError>
Creates a keypair from a 32-byte private key.let private_key: [u8; 32] = /* ... */;
let keypair = Keypair::from_private_key(&private_key)?;
Returns the private key bytes.Warning: Keep private keys secure and never expose them.let keypair = Keypair::generate();
let private_key = keypair.private_key();
Returns the address derived from the public key.let keypair = Keypair::generate();
let address = keypair.address();
sign
fn(&self, message: &[u8]) -> Signature
Signs a message with the private key.let keypair = Keypair::generate();
let message = b"transaction data";
let signature = keypair.sign(message);
sign_hash
fn(&self, hash: &Hash) -> Signature
Signs a hash directly.use minichain_core::hash::hash;
let keypair = Keypair::generate();
let data_hash = hash(b"some data");
let signature = keypair.sign_hash(&data_hash);
verify
fn(&self, message: &[u8], signature: &Signature) -> Result<(), CryptoError>
Verifies a signature against this keypair’s public key.let keypair = Keypair::generate();
let message = b"hello";
let signature = keypair.sign(message);
keypair.verify(message, &signature)?;
The message that was signed
Trait Implementations
- Debug: Formats with the address only (doesn’t expose private key)
Errors
CryptoError
pub enum CryptoError {
InvalidSignature,
InvalidPublicKey,
InvalidPrivateKey,
InvalidAddress,
VerificationFailed,
}
Errors that can occur during cryptographic operations.
The signature format is invalid
The public key format is invalid
The private key format is invalid
The address format is invalid
Signature verification failed (signature doesn’t match message/key)
Usage Examples
Generate a Keypair and Sign
use minichain_core::crypto::Keypair;
// Generate a new keypair
let keypair = Keypair::generate();
println!("Address: {}", keypair.address());
// Sign a message
let message = b"hello world";
let signature = keypair.sign(message);
// Verify the signature
assert!(keypair.verify(message, &signature).is_ok());
Address Derivation
use minichain_core::crypto::Keypair;
// Create a keypair
let keypair = Keypair::generate();
// Get the address
let address = keypair.address();
println!("Address: {}", address.to_hex());
// Address is deterministic from public key
let address2 = keypair.public_key.to_address();
assert_eq!(address, address2);
Restore Keypair from Private Key
use minichain_core::crypto::Keypair;
// Generate and save private key
let keypair1 = Keypair::generate();
let private_key = keypair1.private_key();
// Later, restore from private key
let keypair2 = Keypair::from_private_key(&private_key).unwrap();
// Same address
assert_eq!(keypair1.address(), keypair2.address());
Transaction Signing Pattern
use minichain_core::crypto::Keypair;
use minichain_core::hash::hash;
// Create a transaction
let tx_data = b"from:alice,to:bob,value:100";
let tx_hash = hash(tx_data);
// Sign the transaction hash
let keypair = Keypair::generate();
let signature = keypair.sign_hash(&tx_hash);
// Verify signature
assert!(keypair.public_key.verify(tx_hash.as_bytes(), &signature).is_ok());
Signature Verification with Different Keys
use minichain_core::crypto::Keypair;
let keypair1 = Keypair::generate();
let keypair2 = Keypair::generate();
let message = b"test message";
let signature = keypair1.sign(message);
// Verification succeeds with correct key
assert!(keypair1.verify(message, &signature).is_ok());
// Verification fails with wrong key
assert!(keypair2.verify(message, &signature).is_err());
Implementation Details
Ed25519 Properties
- Algorithm: Twisted Edwards curve signature scheme
- Key Size: 32 bytes (256 bits)
- Signature Size: 64 bytes (512 bits)
- Security: Equivalent to ~128-bit security level
- Performance: Fast signature generation and verification
Address Derivation
Addresses are derived as follows:
- Take the public key (32 bytes)
- Hash it with Blake3 to get 32 bytes
- Take the first 20 bytes as the address
pub fn to_address(&self) -> Address {
let hash = hash(self.0.as_bytes());
let mut addr = [0u8; 20];
addr.copy_from_slice(&hash.0[..20]);
Address(addr)
}
This ensures:
- Addresses are shorter than public keys (20 vs 32 bytes)
- Collision resistance from Blake3
- Deterministic derivation from public key
Security Considerations
- Private Key Storage: Never log or expose private keys
- Random Number Generation: Uses OS-provided cryptographically secure RNG
- Signature Determinism: Same message always produces same signature with same key
- Verification: Always verify signatures before trusting signed data
Source Location
Defined in crates/core/src/crypto.rs