urma-runtime 0.3.4

URMA publication planning, network transports and recovery
Documentation
use crate::config;
use crate::error::{Context, Error};
use crate::publication::quote_record;
use bitcoin::{Txid, hashes::Hash};
use urma_core::multipart::{
    ChildReference, DataPart, Geometry, LeafManifest, MultipartRecord, RootManifest,
};
use urma_identity::identity::IdentitySigner;
use urma_wallet::account::Account;

pub struct Quote {
    pub records: u32,
    pub payload_bytes: u64,
    pub parts: u32,
    pub leaves: u16,
    pub retained_value: u64,
    pub maximum_fee: u64,
    pub funding: u64,
}

pub fn multipart(
    length: u64,
    author: &impl IdentitySigner,
    funder: &Account,
    rate: u64,
) -> Result<Quote, Error> {
    let chain = funder.chain();
    let geometry = Geometry::new(length)?;
    let public = author.public_key().inner.x_only_public_key().0;
    let script = funder.receive_script()?;
    let mut fees = std::collections::BTreeMap::new();
    let mut total = 0u64;
    for index in 0..geometry.nodes() {
        let record = sample(geometry, index)?.encode()?;
        let fee = match fees.get(&record.len()) {
            Some(fee) => *fee,
            None => {
                let fee = quote_record(&record, public, &script, rate)?;
                fees.insert(record.len(), fee);
                fee
            }
        };
        total = total.checked_add(fee).context("quote overflow")?;
    }
    let funding = total
        .checked_add((u64::from(geometry.nodes()) + 1) * config::publication_return(chain))
        .context("quote funding overflow")?;
    Ok(Quote {
        records: geometry.nodes(),
        payload_bytes: length,
        parts: geometry.parts(),
        leaves: geometry.leaves(),
        retained_value: config::publication_return(chain),
        maximum_fee: total,
        funding,
    })
}

fn references(count: usize) -> Result<Vec<ChildReference>, Error> {
    let mut result = Vec::new();
    for index in 0..count {
        let mut id = [0u8; 32];
        id[..8].copy_from_slice(&(u64::try_from(index)? + 1).to_le_bytes());
        result.push(ChildReference {
            txid: Txid::from_byte_array(id),
            record_hash: [0; 32],
        });
    }
    Ok(result)
}

fn sample(geometry: Geometry, index: u32) -> Result<MultipartRecord, Error> {
    if index < geometry.parts() {
        return Ok(MultipartRecord::Data(DataPart {
            index: 0,
            payload: vec![0; geometry.part_length(index)?],
        }));
    }
    let leaf = index - geometry.parts();
    if leaf < u32::from(geometry.leaves()) {
        return Ok(MultipartRecord::Leaf(LeafManifest {
            index: u16::try_from(leaf)?,
            entries: references(usize::from(geometry.leaf_entries(u16::try_from(leaf)?)?))?,
        }));
    }
    Ok(MultipartRecord::Root(RootManifest {
        length: geometry.length(),
        payload_hash: [0; 32],
        profile: [0; 8],
        entries: references(usize::from(geometry.leaves()))?,
    }))
}