use crate::chainparams::ChainParams;
use crate::{BSVErrors, Hash, Script, SighashSignature, BSM};
use crate::{PublicKey, Signature};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct P2PKHAddress(u8, [u8; 20], [u8; 4]);
impl Serialize for P2PKHAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let addr = self.to_string_impl().map_err(|e| serde::ser::Error::custom(e.to_string()))?;
serializer.serialize_str(&addr)
}
}
impl<'de> Deserialize<'de> for P2PKHAddress {
fn deserialize<D>(deserializer: D) -> Result<P2PKHAddress, D::Error>
where
D: Deserializer<'de>,
{
let address_string = String::deserialize(deserializer)?;
let p2pkh = P2PKHAddress::from_string_impl(&address_string).map_err(|e| serde::de::Error::custom(e.to_string()))?;
Ok(p2pkh)
}
}
impl P2PKHAddress {
pub(crate) fn from_pubkey_hash_impl(hash_bytes: &[u8]) -> Result<P2PKHAddress, BSVErrors> {
let mut addr = vec![0x00];
addr.extend_from_slice(hash_bytes);
let shad_bytes = Hash::sha_256d(&addr).to_bytes();
let checksum_bytes = &shad_bytes[0..4];
Ok(P2PKHAddress(
0x00,
hash_bytes.try_into().map_err(BSVErrors::P2PKHAddressFromSlice)?,
checksum_bytes.try_into().map_err(BSVErrors::P2PKHAddressFromSlice)?,
))
}
pub(crate) fn from_pubkey_impl(pub_key: &PublicKey) -> Result<P2PKHAddress, BSVErrors> {
let pub_key_bytes = pub_key.to_bytes_impl()?;
let pub_key_hash = Hash::hash_160(&pub_key_bytes);
P2PKHAddress::from_pubkey_hash_impl(&pub_key_hash.to_bytes())
}
pub fn set_chain_params_impl(&self, chain: &ChainParams) -> Result<P2PKHAddress, BSVErrors> {
let mut addr_bytes = vec![chain.p2pkh];
addr_bytes.extend_from_slice(&self.1);
let checksum = Hash::sha_256d(&addr_bytes).to_bytes()[0..4].to_vec();
let checksum_bytes = [checksum[0], checksum[1], checksum[2], checksum[3]];
Ok(P2PKHAddress(chain.p2pkh, self.1, checksum_bytes))
}
pub(crate) fn to_string_impl(&self) -> Result<String, BSVErrors> {
let mut pub_key_hash_bytes = self.1.to_vec();
let mut address_bytes: Vec<u8> = vec![self.0];
address_bytes.append(&mut pub_key_hash_bytes);
let shad_bytes = Hash::sha_256d(&address_bytes).to_bytes();
let mut checksum_bytes = shad_bytes[0..4].to_vec();
address_bytes.append(&mut checksum_bytes);
let address = bs58::encode(address_bytes);
let addr_string = address.into_string();
Ok(addr_string)
}
pub(crate) fn from_string_impl(address_string: &str) -> Result<P2PKHAddress, BSVErrors> {
if address_string.len() < 33 {
return Err(BSVErrors::P2PKHAddress("Too Short! invalid address"));
}
let decoded = bs58::decode(address_string);
let decoded_bytes = decoded.into_vec()?;
let address_bytes: Vec<u8> = decoded_bytes[..decoded_bytes.len() - 4].to_vec();
let address_checksum = decoded_bytes[decoded_bytes.len() - 4..].to_vec();
let shad_bytes = Hash::sha_256d(&address_bytes).to_bytes();
let checksum_bytes = shad_bytes[0..4].to_vec();
if checksum_bytes != address_checksum {
return Err(BSVErrors::P2PKHAddress("Checksum failed! invalid address."));
}
let chain_byte = decoded_bytes[0];
let pub_key_hash = decoded_bytes[1..decoded_bytes.len() - 4].try_into().map_err(BSVErrors::P2PKHAddressFromSlice)?;
let checksum = decoded_bytes[decoded_bytes.len() - 4..].try_into().map_err(BSVErrors::P2PKHAddressFromSlice)?;
Ok(P2PKHAddress(chain_byte, pub_key_hash, checksum))
}
pub(crate) fn to_locking_script_impl(&self) -> Result<Script, BSVErrors> {
Script::from_asm_string(&format!("OP_DUP OP_HASH160 {} OP_EQUALVERIFY OP_CHECKSIG", self.to_pubkey_hash_hex()))
}
pub(crate) fn to_unlocking_script_impl(&self, pub_key: &PublicKey, sig: &SighashSignature) -> Result<Script, BSVErrors> {
let verifying_address = P2PKHAddress::from_pubkey_impl(pub_key)?;
if verifying_address != *self {
return Err(BSVErrors::GenerateScript("Given public key does not correspond to this address".into()));
}
let pub_key_hex = pub_key.to_hex_impl()?;
let script = Script::from_asm_string(&format!("{} {}", sig.to_hex_impl()?, pub_key_hex))?;
Ok(script)
}
}
impl P2PKHAddress {
pub fn to_pubkey_hash(&self) -> Vec<u8> {
self.1.to_vec()
}
pub fn to_pubkey_hash_hex(&self) -> String {
hex::encode(self.1)
}
pub fn is_valid_bitcoin_message(&self, message: &[u8], signature: &Signature) -> bool {
BSM::verify_message_impl(message, signature, self).is_ok()
}
}
impl P2PKHAddress {
pub fn from_pubkey_hash(hash_bytes: &[u8]) -> Result<P2PKHAddress, BSVErrors> {
P2PKHAddress::from_pubkey_hash_impl(hash_bytes)
}
pub fn from_pubkey(pub_key: &PublicKey) -> Result<P2PKHAddress, BSVErrors> {
P2PKHAddress::from_pubkey_impl(pub_key)
}
pub fn to_string(&self) -> Result<String, BSVErrors> {
P2PKHAddress::to_string_impl(self)
}
pub fn set_chain_params(&self, chain_params: &ChainParams) -> Result<P2PKHAddress, BSVErrors> {
self.set_chain_params_impl(chain_params)
}
pub fn from_string(address_string: &str) -> Result<P2PKHAddress, BSVErrors> {
P2PKHAddress::from_string_impl(address_string)
}
pub fn get_locking_script(&self) -> Result<Script, BSVErrors> {
self.to_locking_script_impl()
}
pub fn get_unlocking_script(&self, pub_key: &PublicKey, sig: &SighashSignature) -> Result<Script, BSVErrors> {
self.to_unlocking_script_impl(pub_key, sig)
}
pub fn verify_bitcoin_message(&self, message: &[u8], signature: &Signature) -> Result<bool, BSVErrors> {
BSM::verify_message_impl(message, signature, self)
}
}