godcoin 0.3.0

GODcoin core blockchain library.
Documentation
use super::*;
use crate::crypto::{double_sha256, Digest, PublicKey};
use crate::script::Script;

pub const SCRIPT_HASH_BUF_PREFIX: u8 = 0x03;

#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ScriptHash(pub Digest);

impl ScriptHash {
    #[inline]
    pub fn from_slice(slice: &[u8]) -> Option<ScriptHash> {
        let digest = Digest::from_slice(slice)?;
        Some(ScriptHash(digest))
    }
}

impl Wif<ScriptHash, Box<str>> for ScriptHash {
    fn from_wif(s: &str) -> Result<ScriptHash, WifError> {
        if s.len() < 3 || &s[0..3] != PUB_ADDRESS_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix));
        }
        let raw = match bs58::decode(&s[3..]).into_vec() {
            Ok(bytes) => bytes,
            Err(_) => {
                return Err(WifError::new(WifErrorKind::InvalidBs58Encoding));
            }
        };
        if raw.len() != 37 {
            return Err(WifError::new(WifErrorKind::InvalidLen));
        } else if raw[0] != SCRIPT_HASH_BUF_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix));
        }

        let prefixed_key = &raw[0..raw.len() - 4];
        {
            let checksum_a = &raw[raw.len() - 4..raw.len()];
            let checksum_b = &double_sha256(prefixed_key)[0..4];
            if checksum_a != checksum_b {
                return Err(WifError::new(WifErrorKind::InvalidChecksum));
            }
        }

        let key = &prefixed_key[1..prefixed_key.len()];
        Ok(ScriptHash::from_slice(key).unwrap())
    }

    fn to_wif(&self) -> Box<str> {
        let mut buf: Vec<u8> = Vec::<u8>::with_capacity(37);
        buf.push(SCRIPT_HASH_BUF_PREFIX);
        buf.extend_from_slice(self.0.as_ref());

        let checksum = &double_sha256(&buf)[0..4];
        buf.extend_from_slice(checksum);

        let mut s = bs58::encode(buf).into_string();
        s.insert_str(0, PUB_ADDRESS_PREFIX);
        s.into_boxed_str()
    }
}

impl From<&Script> for ScriptHash {
    fn from(script: &Script) -> ScriptHash {
        let hash = double_sha256(script);
        ScriptHash(hash)
    }
}

impl From<Script> for ScriptHash {
    fn from(script: Script) -> ScriptHash {
        (&script).into()
    }
}

impl From<PublicKey> for ScriptHash {
    fn from(key: PublicKey) -> ScriptHash {
        let script: Script = key.into();
        script.into()
    }
}

impl From<&PublicKey> for ScriptHash {
    fn from(key: &PublicKey) -> ScriptHash {
        let script: Script = key.clone().into();
        script.into()
    }
}

impl AsRef<[u8]> for ScriptHash {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn import_p2sh_from_wif() {
        let kp =
            PrivateKey::from_wif("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").unwrap();

        let wif = "GOD78WVbdCHAwEVajuPKprZ6je6t1zvTieLEsEcKiYVtTjbpfjqLR";
        let hash = ScriptHash::from_wif(&wif).unwrap();
        assert_eq!(hash.to_wif().as_ref(), wif);
        assert_eq!(ScriptHash::from(Script::from(kp.0.clone())), hash);
    }
}