Skip to content

Commit

Permalink
Use local transaction payload to reattach (#851)
Browse files Browse the repository at this point in the history
* use local transaction payload to reattach

* check messages for conflicts
  • Loading branch information
Thoralf-M authored Jan 5, 2022
1 parent c79da39 commit 88f6202
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changes/reattach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nodejs-binding": patch
---

Use local data when reattaching transactions and check inclusion state for reattached messages.
73 changes: 68 additions & 5 deletions src/account/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2405,6 +2405,10 @@ pub(crate) async fn repost_message(
let message = match account.get_message(message_id).await {
Some(message_to_repost) => {
let mut message_to_repost = message_to_repost.clone();

let client = crate::client::get_client(account.client_options()).await?;
let client = client.read().await;

// check all reattachments of the message we want to promote/rettry/reattach
while let Some(reattachment_message_id) = message_to_repost.reattachment_message_id {
match account.get_message(&reattachment_message_id).await {
Expand All @@ -2414,19 +2418,78 @@ pub(crate) async fn repost_message(
return Err(crate::Error::ClientError(Box::new(
iota_client::Error::NoNeedPromoteOrReattach(message_id.to_string()),
)));
} else {
let metadata = client.get_message().metadata(&reattachment_message_id).await?;
if metadata.conflict_reason.is_some() {
// if the message is conflicting, then any reattachment is also useless, because it
// can't get confirmed or was already confirmed
return Err(crate::Error::ClientError(Box::new(
iota_client::Error::NoNeedPromoteOrReattach(message_id.to_string()),
)));
}
}
}
None => break,
}
}

let client = crate::client::get_client(account.client_options()).await?;
let client = client.read().await;
let metadata = client.get_message().metadata(message_id).await?;
if metadata.conflict_reason.is_some() {
// if the message is conflicting, then any reattachment is also useless, because it can't get confirmed
// or was already confirmed
return Err(crate::Error::ClientError(Box::new(
iota_client::Error::NoNeedPromoteOrReattach(message_id.to_string()),
)));
}

let (id, message) = match action {
RepostAction::Promote => client.promote(message_id).await?,
RepostAction::Reattach => client.reattach(message_id).await?,
RepostAction::Retry => client.retry(message_id).await?,
// Reattach with the local message
RepostAction::Reattach => match client.reattach(message_id).await {
Ok(res) => res,
Err(err) => match err {
iota_client::Error::NoNeedPromoteOrReattach(_) => {
return Err(crate::Error::ClientError(Box::new(
iota_client::Error::NoNeedPromoteOrReattach(message_id.to_string()),
)))
}
// if reattaching with the message from the node failed, we reattach it with the local data
_ => match message_to_repost.payload {
Some(crate::message::MessagePayload::Transaction(tx_payload)) => {
let msg = client
.message()
.finish_message(Some(Payload::Transaction(Box::new(
tx_payload.to_transaction_payload()?,
))))
.await?;
(msg.id().0, msg)
}
_ => return Err(crate::Error::MessageNotFound),
},
},
},
RepostAction::Retry => match client.retry(message_id).await {
Ok(res) => res,
Err(err) => match err {
iota_client::Error::NoNeedPromoteOrReattach(_) => {
return Err(crate::Error::ClientError(Box::new(
iota_client::Error::NoNeedPromoteOrReattach(message_id.to_string()),
)))
}
// if retrying failed, we reattach it with the local data
_ => match message_to_repost.payload {
Some(crate::message::MessagePayload::Transaction(tx_payload)) => {
let msg = client
.message()
.finish_message(Some(Payload::Transaction(Box::new(
tx_payload.to_transaction_payload()?,
))))
.await?;
(msg.id().0, msg)
}
_ => return Err(crate::Error::MessageNotFound),
},
},
},
};
let message = Message::from_iota_message(
id,
Expand Down
2 changes: 2 additions & 0 deletions src/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1959,6 +1959,8 @@ async fn retry_unconfirmed_transactions(synced_accounts: &[SyncedAccount]) -> cr
Err(crate::Error::ClientError(ref e)) => {
if let iota_client::Error::NoNeedPromoteOrReattach(_) = e.as_ref() {
no_need_promote_or_reattach.push(message_id);
} else {
log::debug!("[POLLING] retrying failed: {:?}", e);
}
}
_ => {}
Expand Down
42 changes: 42 additions & 0 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,48 @@ impl MessageTransactionPayload {
unlock_blocks: unlock_blocks.into_boxed_slice(),
})
}

/// Convert to a transaction payload from bee_message
pub fn to_transaction_payload(&self) -> crate::Result<TransactionPayload> {
let mut essence = iota_client::bee_message::payload::transaction::RegularEssenceBuilder::new();
let TransactionEssence::Regular(message_essence) = self.essence();
let mut inputs = Vec::new();
for input in message_essence.inputs() {
if let TransactionInput::Utxo(input) = input {
inputs.push(iota_client::bee_message::input::Input::Utxo(input.input.clone()));
}
}
inputs.sort_unstable_by_key(|a| a.pack_new());
essence = essence.with_inputs(inputs);
let mut outputs = Vec::new();
for output in message_essence.outputs() {
match output {
TransactionOutput::SignatureLockedSingle(output) => {
outputs.push(iota_client::bee_message::output::Output::SignatureLockedSingle(
SignatureLockedSingleOutput::new(output.address.inner, output.amount)?,
))
}
TransactionOutput::SignatureLockedDustAllowance(output) => {
outputs.push(iota_client::bee_message::output::Output::SignatureLockedDustAllowance(
SignatureLockedDustAllowanceOutput::new(output.address.inner, output.amount)?,
))
}
_ => {}
}
}
outputs.sort_unstable_by_key(|a| a.pack_new());
essence = essence.with_outputs(outputs);
if let Some(indexation) = message_essence.payload() {
essence = essence.with_payload(indexation.clone());
}
let essence = essence.finish()?;
Ok(TransactionPayload::builder()
.with_essence(Essence::Regular(essence))
.with_unlock_blocks(iota_client::bee_message::unlock::UnlockBlocks::new(
self.unlock_blocks.to_vec(),
)?)
.finish()?)
}
}

/// Milestone payload essence.
Expand Down

0 comments on commit 88f6202

Please sign in to comment.