Skip to content

Commit

Permalink
StrongParents, WeakParents and ShallowLikeParents (#697)
Browse files Browse the repository at this point in the history
* StrongParents, WeakParents and ShallowLikeParents

* Fix
  • Loading branch information
thibault-martinez committed Jul 17, 2023
1 parent 5ca9a9e commit 5af46be
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 103 deletions.
4 changes: 2 additions & 2 deletions sdk/examples/client/block/02_block_custom_parents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use iota_sdk::{
client::{Client, Result},
types::block::parent::Parents,
types::block::parent::StrongParents,
};

#[tokio::main]
Expand All @@ -29,7 +29,7 @@ async fn main() -> Result<()> {

// Create and send the block with custom parents.
let block = client
.finish_block_builder(Some(Parents::from_vec(tips)?), None)
.finish_block_builder(Some(StrongParents::from_vec(parents)?), None)
.await?;

println!("{block:#?}");
Expand Down
60 changes: 36 additions & 24 deletions sdk/src/client/api/block_builder/pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,49 @@ use crate::pow::miner::{Miner, MinerBuilder, MinerCancel};
use crate::pow::wasm_miner::{SingleThreadedMiner, SingleThreadedMinerBuilder};
use crate::{
client::{ClientInner, Error, Result},
types::block::{parent::Parents, payload::Payload, Block, BlockBuilder, Error as BlockError},
types::block::{parent::StrongParents, payload::Payload, Block, BlockBuilder, Error as BlockError},
};

impl ClientInner {
/// Finishes the block with local PoW if needed.
/// Without local PoW, it will finish the block with a 0 nonce.
pub async fn finish_block_builder(&self, parents: Option<Parents>, payload: Option<Payload>) -> Result<Block> {
pub async fn finish_block_builder(
&self,
strong_parents: Option<StrongParents>,
payload: Option<Payload>,
) -> Result<Block> {
if self.get_local_pow().await {
self.finish_pow(parents, payload).await
self.finish_pow(strong_parents, payload).await
} else {
// Finish block without doing PoW.
let parents = match parents {
Some(parents) => parents,
None => Parents::from_vec(self.get_tips().await?)?,
let strong_parents = match strong_parents {
Some(strong_parents) => strong_parents,
None => StrongParents::from_vec(self.get_tips().await?)?,
};

Ok(BlockBuilder::new(parents).with_payload(payload).finish()?)
Ok(BlockBuilder::new(strong_parents).with_payload(payload).finish()?)
}
}

/// Calls the appropriate PoW function depending whether the compilation is for wasm or not.
pub async fn finish_pow(&self, parents: Option<Parents>, payload: Option<Payload>) -> Result<Block> {
pub async fn finish_pow(&self, strong_parents: Option<StrongParents>, payload: Option<Payload>) -> Result<Block> {
#[cfg(not(target_family = "wasm"))]
let block = self.finish_multi_threaded_pow(parents, payload).await?;
let block = self.finish_multi_threaded_pow(strong_parents, payload).await?;
#[cfg(target_family = "wasm")]
let block = self.finish_single_threaded_pow(parents, payload).await?;
let block = self.finish_single_threaded_pow(strong_parents, payload).await?;

Ok(block)
}

/// Performs multi-threaded proof-of-work.
///
/// Always fetches new tips after each tips interval elapses if no parents are provided.
/// Always fetches new tips after each tips interval elapses if no strong parents are provided.
#[cfg(not(target_family = "wasm"))]
async fn finish_multi_threaded_pow(&self, parents: Option<Parents>, payload: Option<Payload>) -> Result<Block> {
async fn finish_multi_threaded_pow(
&self,
strong_parents: Option<StrongParents>,
payload: Option<Payload>,
) -> Result<Block> {
let pow_worker_count = *self.pow_worker_count.read().await;
let min_pow_score = self.get_min_pow_score().await?;
let tips_interval = self.get_tips_interval().await;
Expand All @@ -52,17 +60,17 @@ impl ClientInner {
let cancel = MinerCancel::new();
let cancel_2 = cancel.clone();
let payload_ = payload.clone();
let parents = match &parents {
Some(parents) => parents.clone(),
None => Parents::from_vec(self.get_tips().await?)?,
let strong_parents = match &strong_parents {
Some(strong_parents) => strong_parents.clone(),
None => StrongParents::from_vec(self.get_tips().await?)?,
};
let time_thread = std::thread::spawn(move || Ok(pow_timeout(tips_interval, cancel)));
let pow_thread = std::thread::spawn(move || {
let mut client_miner = MinerBuilder::new().with_cancel(cancel_2);
if let Some(worker_count) = pow_worker_count {
client_miner = client_miner.with_num_workers(worker_count);
}
do_pow(client_miner.finish(), min_pow_score, payload_, parents).map(Some)
do_pow(client_miner.finish(), min_pow_score, payload_, strong_parents).map(Some)
});

for t in [pow_thread, time_thread] {
Expand All @@ -84,23 +92,27 @@ impl ClientInner {
/// Single threaded proof-of-work for Wasm, which cannot generally spawn the native threads used
/// by the `ClientMiner`.
///
/// Fetches new tips after each tips interval elapses if no parents are provided.
/// Fetches new tips after each tips interval elapses if no strong parents are provided.
#[cfg(target_family = "wasm")]
async fn finish_single_threaded_pow(&self, parents: Option<Parents>, payload: Option<Payload>) -> Result<Block> {
async fn finish_single_threaded_pow(
&self,
strong_parents: Option<StrongParents>,
payload: Option<Payload>,
) -> Result<Block> {
let min_pow_score: u32 = self.get_min_pow_score().await?;
let tips_interval: u64 = self.get_tips_interval().await;

loop {
let parents = match &parents {
Some(parents) => parents.clone(),
None => Parents::from_vec(self.get_tips().await?)?,
let strong_parents = match &strong_parents {
Some(strong_parents) => strong_parents.clone(),
None => StrongParents::from_vec(self.get_tips().await?)?,
};

let single_threaded_miner = SingleThreadedMinerBuilder::new()
.with_timeout_in_seconds(tips_interval)
.finish();

match do_pow(single_threaded_miner, min_pow_score, payload.clone(), parents) {
match do_pow(single_threaded_miner, min_pow_score, payload.clone(), strong_parents) {
Ok(block) => {
return Ok(block);
}
Expand All @@ -119,9 +131,9 @@ fn do_pow(
#[cfg(target_family = "wasm")] miner: SingleThreadedMiner,
min_pow_score: u32,
payload: Option<Payload>,
parents: Parents,
strong_parents: StrongParents,
) -> Result<Block> {
Ok(BlockBuilder::new(parents)
Ok(BlockBuilder::new(strong_parents)
.with_payload(payload)
.finish_nonce(|bytes| miner.nonce(bytes, min_pow_score))?)
}
Expand Down
52 changes: 26 additions & 26 deletions sdk/src/types/block/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use packable::{
};

use crate::types::block::{
parent::Parents,
parent::StrongParents,
payload::{OptionalPayload, Payload},
protocol::ProtocolParameters,
BlockId, Error, PROTOCOL_VERSION,
Expand All @@ -24,7 +24,7 @@ use crate::types::block::{
#[must_use]
pub struct BlockBuilder {
protocol_version: Option<u8>,
parents: Parents,
strong_parents: StrongParents,
payload: OptionalPayload,
nonce: Option<u64>,
}
Expand All @@ -34,10 +34,10 @@ impl BlockBuilder {

/// Creates a new [`BlockBuilder`].
#[inline(always)]
pub fn new(parents: Parents) -> Self {
pub fn new(strong_parents: StrongParents) -> Self {
Self {
protocol_version: None,
parents,
strong_parents,
payload: OptionalPayload::default(),
nonce: None,
}
Expand Down Expand Up @@ -69,7 +69,7 @@ impl BlockBuilder {

let block = Block {
protocol_version: self.protocol_version.unwrap_or(PROTOCOL_VERSION),
parents: self.parents,
strong_parents: self.strong_parents,
payload: self.payload,
nonce: self.nonce.unwrap_or(Self::DEFAULT_NONCE),
};
Expand Down Expand Up @@ -105,8 +105,8 @@ impl BlockBuilder {
pub struct Block {
/// Protocol version of the block.
protocol_version: u8,
/// The [`BlockId`]s that this block directly approves.
parents: Parents,
/// Blocks that are strongly directly approved.
strong_parents: StrongParents,
/// The optional [Payload] of the block.
payload: OptionalPayload,
/// The result of the Proof of Work in order for the block to be accepted into the tangle.
Expand All @@ -121,8 +121,8 @@ impl Block {

/// Creates a new [`BlockBuilder`] to construct an instance of a [`Block`].
#[inline(always)]
pub fn build(parents: Parents) -> BlockBuilder {
BlockBuilder::new(parents)
pub fn build(strong_parents: StrongParents) -> BlockBuilder {
BlockBuilder::new(strong_parents)
}

/// Returns the protocol version of a [`Block`].
Expand All @@ -131,10 +131,10 @@ impl Block {
self.protocol_version
}

/// Returns the parents of a [`Block`].
/// Returns the strong parents of a [`Block`].
#[inline(always)]
pub fn parents(&self) -> &Parents {
&self.parents
pub fn strong_parents(&self) -> &StrongParents {
&self.strong_parents
}

/// Returns the optional payload of a [`Block`].
Expand All @@ -155,10 +155,10 @@ impl Block {
BlockId::new(Blake2b256::digest(self.pack_to_vec()).into())
}

/// Consumes the [`Block`], and returns ownership over its [`Parents`].
/// Consumes the [`Block`], and returns ownership over its [`StrongParents`].
#[inline(always)]
pub fn into_parents(self) -> Parents {
self.parents
pub fn into_strong_parents(self) -> StrongParents {
self.strong_parents
}

/// Unpacks a [`Block`] from a sequence of bytes doing syntactical checks and verifying that
Expand All @@ -185,7 +185,7 @@ impl Packable for Block {

fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
self.protocol_version.pack(packer)?;
self.parents.pack(packer)?;
self.strong_parents.pack(packer)?;
self.payload.pack(packer)?;
self.nonce.pack(packer)?;

Expand All @@ -207,7 +207,7 @@ impl Packable for Block {
}));
}

let parents = Parents::unpack::<_, VERIFY>(unpacker, &())?;
let strong_parents = StrongParents::unpack::<_, VERIFY>(unpacker, &())?;
let payload = OptionalPayload::unpack::<_, VERIFY>(unpacker, visitor)?;

if VERIFY {
Expand All @@ -218,7 +218,7 @@ impl Packable for Block {

let block = Self {
protocol_version,
parents,
strong_parents,
payload,
nonce,
};
Expand Down Expand Up @@ -268,7 +268,7 @@ pub mod dto {
///
pub protocol_version: u8,
///
pub parents: Vec<String>,
pub strong_parents: Vec<String>,
///
#[serde(skip_serializing_if = "Option::is_none")]
pub payload: Option<PayloadDto>,
Expand All @@ -280,7 +280,7 @@ pub mod dto {
fn from(value: &Block) -> Self {
Self {
protocol_version: value.protocol_version(),
parents: value.parents().iter().map(BlockId::to_string).collect(),
strong_parents: value.strong_parents().iter().map(BlockId::to_string).collect(),
payload: value.payload().map(Into::into),
nonce: value.nonce().to_string(),
}
Expand All @@ -289,15 +289,15 @@ pub mod dto {

impl Block {
pub fn try_from_dto(value: BlockDto, protocol_parameters: &ProtocolParameters) -> Result<Self, Error> {
let parents = Parents::from_vec(
let strong_parents = StrongParents::from_vec(
value
.parents
.strong_parents
.into_iter()
.map(|m| m.parse::<BlockId>().map_err(|_| Error::InvalidField("parents")))
.collect::<Result<Vec<BlockId>, Error>>()?,
)?;

let mut builder = BlockBuilder::new(parents)
let mut builder = BlockBuilder::new(strong_parents)
.with_protocol_version(value.protocol_version)
.with_nonce(value.nonce.parse::<u64>().map_err(|_| Error::InvalidField("nonce"))?);

Expand All @@ -309,15 +309,15 @@ pub mod dto {
}

pub fn try_from_dto_unverified(value: BlockDto) -> Result<Self, Error> {
let parents = Parents::from_vec(
let strong_parents = StrongParents::from_vec(
value
.parents
.strong_parents
.into_iter()
.map(|m| m.parse::<BlockId>().map_err(|_| Error::InvalidField("parents")))
.collect::<Result<Vec<BlockId>, Error>>()?,
)?;

let mut builder = BlockBuilder::new(parents)
let mut builder = BlockBuilder::new(strong_parents)
.with_protocol_version(value.protocol_version)
.with_nonce(value.nonce.parse::<u64>().map_err(|_| Error::InvalidField("nonce"))?);

Expand Down
11 changes: 7 additions & 4 deletions sdk/src/types/block/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use alloc::string::{FromUtf8Error, String};
use core::{convert::Infallible, fmt};

use crypto::Error as CryptoError;
// use packable::bounded::BoundedU8;
use prefix_hex::Error as HexError;
use primitive_types::U256;

Expand All @@ -14,7 +15,6 @@ use crate::types::block::{
feature::FeatureCount, unlock_condition::UnlockConditionCount, AliasId, ChainId, MetadataFeatureLength,
NativeTokenCount, NftId, OutputIndex, StateMetadataLength, TagFeatureLength,
},
parent::ParentCount,
payload::{InputCount, OutputCount, TagLength, TaggedDataLength},
unlock::{UnlockCount, UnlockIndex},
};
Expand Down Expand Up @@ -59,7 +59,10 @@ pub enum Error {
InvalidOutputAmount(u64),
InvalidOutputCount(<OutputCount as TryFrom<usize>>::Error),
InvalidOutputKind(u8),
InvalidParentCount(<ParentCount as TryFrom<usize>>::Error),
// TODO this would now need to be generic, not sure if possible.
// https://github.com/iotaledger/iota-sdk/issues/647
// InvalidParentCount(<BoundedU8 as TryFrom<usize>>::Error),
InvalidParentCount,
InvalidPayloadKind(u32),
InvalidPayloadLength { expected: usize, actual: usize },
InvalidReferenceIndex(<UnlockIndex as TryFrom<u16>>::Error),
Expand Down Expand Up @@ -182,8 +185,8 @@ impl fmt::Display for Error {
Self::InvalidOutputAmount(amount) => write!(f, "invalid output amount: {amount}"),
Self::InvalidOutputCount(count) => write!(f, "invalid output count: {count}"),
Self::InvalidOutputKind(k) => write!(f, "invalid output kind: {k}"),
Self::InvalidParentCount(count) => {
write!(f, "invalid parents count: {count}")
Self::InvalidParentCount => {
write!(f, "invalid parents count")
}
Self::InvalidPayloadKind(k) => write!(f, "invalid payload kind: {k}"),
Self::InvalidPayloadLength { expected, actual } => {
Expand Down
Loading

0 comments on commit 5af46be

Please sign in to comment.