stacks-rs 0.1.4

Rust toolkit to interact with the Stacks Blockchain.
Documentation
use crate::crypto::hash::Sha512_256Hash;
use crate::crypto::impl_wrapped_array;
use crate::crypto::Serialize;
use crate::network::ChainID;
use crate::network::TransactionVersion;
use crate::transaction::auth::Authorization;
use crate::transaction::auth::AuthorizationType;
use crate::transaction::auth::MessageSignature;
use crate::transaction::auth::SpendingCondition;
use crate::transaction::auth::PUBLIC_KEY_ENCODING;
use crate::transaction::condition::AnchorMode;
use crate::transaction::condition::PostConditionMode;
use crate::transaction::condition::PostConditions;
use crate::transaction::Error;
use crate::transaction::Payload;
use crate::StacksPrivateKey;
use crate::StacksPublicKey;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TransactionId([u8; 32]);
impl_wrapped_array!(TransactionId, u8, 32);

impl TransactionId {
    pub fn from_slice(bytes: &[u8]) -> Self {
        let hasher = Sha512_256Hash::from_slice(bytes);
        TransactionId(hasher.into_bytes())
    }

    pub fn make_presign_hash(
        sighash: TransactionId,
        auth_type: AuthorizationType,
        tx_fee: u64,
        nonce: u64,
    ) -> Self {
        let mut buff = vec![];

        buff.extend_from_slice(&sighash.to_bytes());
        buff.push(auth_type as u8);
        buff.extend_from_slice(&tx_fee.to_be_bytes());
        buff.extend_from_slice(&nonce.to_be_bytes());

        Self::from_slice(&buff)
    }

    pub fn make_postsign_hash(sighash: TransactionId, signature: MessageSignature) -> Self {
        let mut buff = vec![];

        buff.extend_from_slice(&sighash.to_bytes());
        buff.push(PUBLIC_KEY_ENCODING);
        buff.extend_from_slice(&signature.to_bytes());

        Self::from_slice(&buff)
    }

    pub fn next_signature(
        sighash: TransactionId,
        auth_type: AuthorizationType,
        tx_fee: u64,
        nonce: u64,
        private_key: &StacksPrivateKey,
    ) -> Result<(MessageSignature, Self), Error> {
        let secp = secp256k1::Secp256k1::new();
        let presgn = Self::make_presign_hash(sighash, auth_type, tx_fee, nonce);

        let signature = {
            let msg = secp256k1::Message::from_slice(&presgn.to_bytes())?;
            let recov = secp.sign_ecdsa_recoverable(&msg, private_key);
            MessageSignature::from_recov(recov)?
        };

        let next_sighash = Self::make_postsign_hash(presgn, signature);
        Ok((signature, next_sighash))
    }

    pub fn next_verification(
        sighash: TransactionId,
        auth_type: AuthorizationType,
        tx_fee: u64,
        nonce: u64,
        signature: MessageSignature,
    ) -> Result<(StacksPublicKey, Self), Error> {
        let secp = secp256k1::Secp256k1::new();
        let presgn = Self::make_presign_hash(sighash, auth_type, tx_fee, nonce);

        let recov = signature.into_recov()?;
        let msg = secp256k1::Message::from_slice(&presgn.to_bytes())?;
        let pubk = secp.recover_ecdsa(&msg, &recov)?;

        let next_sighash = Self::make_postsign_hash(presgn, signature);

        Ok((pubk, next_sighash))
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct StacksTransaction {
    pub version: TransactionVersion,
    pub chain_id: ChainID,
    pub auth: Authorization,
    pub anchor_mode: AnchorMode,
    pub post_condition_mode: PostConditionMode,
    pub post_conditions: PostConditions,
    pub payload: Payload,
}

impl StacksTransaction {
    pub fn new(
        version: TransactionVersion,
        chain_id: ChainID,
        auth: Authorization,
        anchor_mode: AnchorMode,
        post_condition_mode: PostConditionMode,
        post_conditions: PostConditions,
        payload: Payload,
    ) -> Self {
        Self {
            version,
            chain_id,
            auth,
            anchor_mode,
            post_condition_mode,
            post_conditions,
            payload,
        }
    }

    pub fn tx_id(&self) -> Result<TransactionId, Error> {
        let tx_bytes = self.serialize()?;
        let tx_id = TransactionId::from_slice(&tx_bytes);
        Ok(tx_id)
    }

    pub fn initial_sighash(&self) -> Result<TransactionId, Error> {
        let mut tx = self.clone();
        tx.auth = tx.auth.into_initial_hash();
        tx.tx_id()
    }

    pub fn verify(&self) -> Result<(), Error> {
        self.auth.verify(self.initial_sighash()?)
    }

    pub fn verify_origin(&self) -> Result<TransactionId, Error> {
        self.auth.verify_origin(self.initial_sighash()?)
    }

    pub fn set_sponsor(&mut self, sponsor: SpendingCondition) -> Result<(), Error> {
        self.auth.set_sponsor(sponsor)
    }

    pub fn set_fee(&mut self, fee: u64) {
        self.auth.set_fee(fee);
    }

    pub fn set_nonce(&mut self, nonce: u64) {
        self.auth.set_nonce(nonce);
    }
}

impl Serialize for StacksTransaction {
    type Err = Error;

    fn serialize(&self) -> Result<Vec<u8>, Self::Err> {
        let mut buffer = vec![];

        buffer.push(self.version as u8);
        buffer.extend_from_slice(&(self.chain_id as u32).to_be_bytes());
        buffer.extend_from_slice(&self.auth.serialize()?);
        buffer.push(self.anchor_mode as u8);
        buffer.push(self.post_condition_mode as u8);
        buffer.extend_from_slice(&self.post_conditions.serialize()?);
        buffer.extend_from_slice(&self.payload.serialize()?);

        Ok(buffer)
    }
}