dig-store-coin 0.1.0

A wrapper library over the datalayer driver for managing DIG network store coins on chain.
Documentation
use datalayer_driver::async_api::get_all_unspent_coins;
use datalayer_driver::wallet::{broadcast_spend_bundle};
use datalayer_driver::{Bytes32, Coin, DataStore, DataStoreInnerSpend, NetworkType, PublicKey, SecretKey, SpendBundle, SuccessResponse, admin_delegated_puzzle_from_key, connect_random, get_fee_estimate, get_header_hash, mint_store, oracle_delegated_puzzle, secret_key_to_public_key, select_coins, sign_coin_spends, synthetic_key_to_puzzle_hash, update_store_metadata, writer_delegated_puzzle_from_key, add_fee};

static MIN_HEIGHT: u32 = 5777842;

#[derive(Debug, Clone)]
pub struct MintParams<'mp> {
    pub ssl_cert_path: &'mp str,
    pub ssl_key_path: &'mp str,
    pub root_hash: Bytes32,
    pub owner_secret_key: SecretKey,
    pub network: NetworkType,
    pub previous_height: Option<u32>,
    pub label: Option<String>,
    pub description: Option<String>,
    pub size_in_bytes: Option<u64>,
    pub size_proof: Option<String>,
    pub writer_public_synthetic_key: Option<PublicKey>,
    pub admin_public_synthetic_key: Option<PublicKey>,
    pub fee: Option<u64>,
}

pub async fn mint(params: MintParams<'_>) -> datalayer_driver::Result<DataStore> {
    let peer = connect_random(params.network, params.ssl_cert_path, params.ssl_key_path).await?;

    let owner_public_key = secret_key_to_public_key(&params.owner_secret_key);
    let owner_puzzle_hash = synthetic_key_to_puzzle_hash(&owner_public_key);

    let height = if let Some(specified_height) = params.previous_height {specified_height} else {MIN_HEIGHT};
    let owner_previous_header_hash = get_header_hash(&peer, height).await?;

    // todo get clarification on oracle fee in relation to mint fee
    let mut delegated_puzzles = vec![oracle_delegated_puzzle(owner_puzzle_hash, 100_000)];

    if let Some(key) = params.admin_public_synthetic_key {
        delegated_puzzles.push(admin_delegated_puzzle_from_key(&key))
    }

    if let Some(key) = params.writer_public_synthetic_key {
        delegated_puzzles.push(writer_delegated_puzzle_from_key(&key))
    }

    let fee = if let Some(specified_fee) = params.fee {
        specified_fee
    } else {
        get_fee_estimate(&peer, 60).await?
    };
    let required_wallet_balance = fee + 1;

    let unspent_coin_states = get_all_unspent_coins(
        &peer,
        owner_puzzle_hash,
        Some(height),
        owner_previous_header_hash,
    )
    .await?;

    let unspent_coins = unspent_coin_states
        .coin_states
        .iter()
        .map(|coin_state| coin_state.coin)
        .collect::<Vec<Coin>>();
    let selected_coins = select_coins(&unspent_coins, required_wallet_balance)?;

    let SuccessResponse {
        coin_spends,
        new_datastore,
    } = mint_store(
        owner_public_key,
        selected_coins,
        params.root_hash,
        params.label,
        params.description,
        params.size_in_bytes,
        params.size_proof,
        owner_puzzle_hash,
        delegated_puzzles,
        fee,
    )?;

    let sign_testnet = if params.network == NetworkType::Testnet11 {
        true
    } else {
        false
    };
    let signature = sign_coin_spends(&coin_spends, &[params.owner_secret_key], sign_testnet)?;
    let spend_bundle = SpendBundle::new(coin_spends, signature);
    broadcast_spend_bundle(&peer, spend_bundle).await?;

    Ok(new_datastore)
}

#[derive(Debug, Clone)]
pub struct UpdateParams<'up> {
    pub ssl_cert_path: &'up str,
    pub ssl_key_path: &'up str,
    pub data_store: DataStore,
    pub owner_secret_key: SecretKey,
    pub new_root_hash: Bytes32,
    pub network: NetworkType,
    pub previous_height: Option<u32>,
    pub new_label: Option<String>,
    pub new_description: Option<String>,
    pub new_size_in_bytes: Option<u64>,
    pub new_size_proof: Option<String>,
    pub admin_or_writer_public_key: Option<DataStoreInnerSpend>,
    pub fee: Option<u64>,
}

pub async fn update(
    params: UpdateParams<'_>,
) -> datalayer_driver::Result<DataStore> {
    let peer = connect_random(params.network, params.ssl_cert_path, params.ssl_key_path).await?;
    let owner_public_key = secret_key_to_public_key(&params.owner_secret_key);
    let inner_spend = if let Some(key) = params.admin_or_writer_public_key {
        key
    } else {
        DataStoreInnerSpend::Owner(owner_public_key)
    };

    let SuccessResponse {
        coin_spends: update_store_coin_spends,
        new_datastore: updated_datastore,
    } = update_store_metadata(
        params.data_store,
        params.new_root_hash,
        params.new_label,
        params.new_description,
        params.new_size_in_bytes,
        params.new_size_proof,
        inner_spend,
    )?;

    let fee = if let Some(specified_fee) = params.fee {
        specified_fee
    } else {
        get_fee_estimate(&peer, 60).await?
    };

    let height = if let Some(specified_height) = params.previous_height {specified_height} else {MIN_HEIGHT};
    let owner_puzzle_hash = synthetic_key_to_puzzle_hash(&owner_public_key);
    let owner_previous_header_hash = get_header_hash(&peer, height).await?;

    let unspent_coin_states = get_all_unspent_coins(
        &peer,
        owner_puzzle_hash,
        Some(height),
        owner_previous_header_hash,
    )
        .await?;

    let unspent_coins = unspent_coin_states
        .coin_states
        .iter()
        .map(|coin_state| coin_state.coin)
        .collect::<Vec<Coin>>();
    let selected_coins = select_coins(&unspent_coins, fee)?;

    let coin_ids: Vec<Bytes32> = selected_coins.iter().map(|coin| coin.coin_id()).collect();
    let mut coin_spends = add_fee(&owner_public_key, &selected_coins, &coin_ids, fee)?;
    coin_spends.extend(update_store_coin_spends);


    let sign_testnet = if params.network == NetworkType::Testnet11 {
        true
    } else {
        false
    };
    let signature = sign_coin_spends(&coin_spends, &[params.owner_secret_key], sign_testnet)?;
    let spend_bundle = SpendBundle::new(coin_spends, signature);
    broadcast_spend_bundle(&peer, spend_bundle).await?;

    Ok(updated_datastore)
}