iotaledger / iota-sdk

The IOTA SDK provides developers with a seamless experience to develop on IOTA by providing account abstractions and clients to interact with node APIs.
Apache License 2.0
57 stars 41 forks source link

Handle failed transactions/blocks #2120

Closed Thoralf-M closed 6 months ago

Thoralf-M commented 7 months ago

Description of change

Handle issues from posting the block, if we can't get it posted 3 times, we will return None for the block id and during syncing when we also can't get the block, we will set the tx state as conflicting and unlock its inputs so they're available for new transactions again.

Links to any relevant issues

https://github.com/iotaledger/iota-sdk/issues/2111

Type of change

How the change has been tested

cli wallet and

#[tokio::test]
async fn send_amount_in_loop() -> Result<(), Box<dyn std::error::Error>> {
    let logger_output_config = fern_logger::LoggerOutputConfigBuilder::new()
        .name("wallet.log")
        .target_exclusions(&["h2", "hyper", "rustls"])
        .level_filter(log::LevelFilter::Debug);
    let config = fern_logger::LoggerConfig::build()
        .with_output(logger_output_config)
        .finish();
    fern_logger::logger_init(config).unwrap();

    let storage_path = "test-storage/send_amount";
    setup(storage_path)?;

    println!("Creating wallet with block issuer account...");
    let wallet_0 = make_wallet(storage_path, None, None).await?;
    request_funds(&wallet_0).await?;

    for i in 0..1000 {
        match wallet_0
            .send_with_params([SendParams::new(1_000_000, wallet_0.address().await)?], None)
            .await
        {
            Ok(tx) => {
                println!("Sent tx {i}");
                if let Some(block_id) = tx.block_id {
                    println!(" in block {block_id}");
                    wallet_0
                        .wait_for_transaction_acceptance(&tx.transaction_id, None, None)
                        .await?;
                }
            }
            Err(err) => {
                println!("{err:?}");
            }
        }
        wallet_0.sync(None).await.unwrap();
    }

    tear_down(storage_path)
}

with a modified operations/block.rs so we get the case where no block was sent

        let block_id = if crate::types::block::rand::bool::rand_bool() {
            self.client().post_block(&block).await?
        } else {
            // Just so it returns an error for testing, not relevant
            return Err(WalletError::InvalidVotingPower);
        };