iota-sdk 1.0.1

The IOTA SDK provides developers with a seamless experience to develop on IOTA by providing account abstractions and clients to interact with node APIs.
Documentation
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Contains utilities to score Proof of Work.

use crypto::{
    encoding::ternary::{b1t6, Btrit, T1B1Buf, TritBuf, Trits, T1B1},
    hashes::{
        blake2b::Blake2b256,
        ternary::{curl_p::CurlP, HASH_LENGTH},
        Digest,
    },
};

/// Encapsulates the different steps that are used for scoring Proof of Work.
pub struct PowScorer {
    blake2b: Blake2b256,
    pow_input: TritBuf<T1B1Buf>,
    curl: CurlP,
}

impl Default for PowScorer {
    fn default() -> Self {
        Self::new()
    }
}

impl PowScorer {
    /// Creates an new `PowScorer` that holds the required hash functions as internal state.
    pub fn new() -> Self {
        Self {
            blake2b: Blake2b256::new(),
            pow_input: TritBuf::<T1B1Buf>::with_capacity(HASH_LENGTH),
            curl: CurlP::new(),
        }
    }

    /// Returns the Proof of Work hash of given bytes.
    /// Panic: expects at least 8 bytes.
    pub fn hash(&mut self, bytes: &[u8]) -> TritBuf<T1B1Buf> {
        debug_assert!(bytes.len() >= std::mem::size_of::<u8>());

        // Compute Blake2b-256 hash of the block, excluding the nonce.
        let length = bytes.len() - std::mem::size_of::<u64>();
        let (head, tail) = bytes.split_at(length);
        self.blake2b.update(head);
        let pow_digest = self.blake2b.finalize_reset();

        // Encode block as trits
        self.pow_input.clear();
        self.pow_input.append(b1t6::encode::<T1B1Buf>(&pow_digest).as_slice());
        self.pow_input.append(b1t6::encode::<T1B1Buf>(tail).as_slice());

        // Pad to 243 trits
        self.pow_input.push(Btrit::Zero);
        self.pow_input.push(Btrit::Zero);
        self.pow_input.push(Btrit::Zero);

        self.curl.digest(self.pow_input.as_slice())
    }

    /// Computes the Proof of Work score of given bytes.
    /// Panic: expects at least 8 bytes.
    pub fn score(&mut self, bytes: &[u8]) -> f64 {
        pow_score_for_hash(&self.hash(bytes), bytes.len())
    }
}

/// Returns the number of trailing zeros of a Proof of Work hash.
pub fn count_trailing_zeros(pow_hash: &Trits<T1B1>) -> usize {
    pow_hash.iter().rev().take_while(|t| *t == Btrit::Zero).count()
}

/// Returns the Proof of Work score of a Proof of Work hash.
pub fn pow_score_for_hash(pow_hash: &Trits<T1B1>, len: usize) -> f64 {
    3u128.pow(count_trailing_zeros(pow_hash) as u32) as f64 / len as f64
}