diff --git a/Cargo.lock b/Cargo.lock
index a8e8112d3a50e773bdb65f1561e2d29cfbf49b21..bea864b280641d246b2d9a55f79345b04a3fee05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -958,9 +958,9 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
 
 [[package]]
 name = "dubp"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2a6192b0248f4b198e9193a59ee73e26ee2feba9b7c4995e628d11d2c19448a"
+checksum = "362f2f4944b4c83949cfe91a3c2cf357d45e484ad93719a72a823e4a8e9f57c4"
 dependencies = [
  "dubp-block",
  "dubp-common",
@@ -972,9 +972,9 @@ dependencies = [
 
 [[package]]
 name = "dubp-block"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f59b0253ba66aee87741603f20bab7d67e2ebe11f2bbff58bbc08486b132bb2"
+checksum = "02bf4bd694cb294032e23b8a4dd7f33e69c16cf0cc43d0cea930ea6ec05c17b0"
 dependencies = [
  "dubp-documents",
  "dubp-documents-parser",
@@ -987,9 +987,9 @@ dependencies = [
 
 [[package]]
 name = "dubp-common"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a927b5f18b8d5f04b6d1ca179a5678f5f559140844268f78c6872d20fd6f6178"
+checksum = "fa7ad811ff15659e90561b028ff7cdf57b8e05cf647a421f7014202722567a82"
 dependencies = [
  "dup-crypto",
  "serde",
@@ -1000,9 +1000,9 @@ dependencies = [
 
 [[package]]
 name = "dubp-documents"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccce9aabcb92de2befbbd4360fc73d806df63dce414274bef427de2a7acb725"
+checksum = "42a01990f2234f853e8768e5403ec89e02afea47760e3644fe59153558d328d9"
 dependencies = [
  "beef",
  "dubp-wallet",
@@ -1014,9 +1014,9 @@ dependencies = [
 
 [[package]]
 name = "dubp-documents-parser"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abaa735586c05cc4cd1f240eb9531a9762d97e0f55a335e6e11658f4b10f29af"
+checksum = "d7221d36592e369527a832f1f4b5fa3ff11e7d68467a2384d1d5fc6d7728159d"
 dependencies = [
  "dubp-documents",
  "json-pest-parser",
@@ -1028,9 +1028,9 @@ dependencies = [
 
 [[package]]
 name = "dubp-wallet"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b73e3ae439e2eba8048ca9830a821cd6cbb51010e0049efc72e201433e6a61d"
+checksum = "1e9ef70cc701226d3610452284637f0c86412f5d1a5831f6147964a23366e409"
 dependencies = [
  "byteorder",
  "dubp-common",
@@ -1124,6 +1124,7 @@ dependencies = [
  "duniter-dbs",
  "fast-threadpool",
  "log",
+ "maplit",
  "resiter",
  "serde_json",
 ]
@@ -1292,9 +1293,9 @@ dependencies = [
 
 [[package]]
 name = "dup-crypto"
-version = "0.33.0"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e577ae936c53b85818749d1eefedf1d1afd6e0a8286b88543cb068396c16069"
+checksum = "2e1f1a33a3c3c66c0f2e1e050e2dbd8291975308eac2f7a1a58732f58a21c2e5"
 dependencies = [
  "base64",
  "bs58",
diff --git a/neon/native/Cargo.toml b/neon/native/Cargo.toml
index 569c57566f66f1602be278f5ff7a0e5e5e15d058..90daffb97f10896e1c7c93b6282cba5abebd15eb 100644
--- a/neon/native/Cargo.toml
+++ b/neon/native/Cargo.toml
@@ -17,7 +17,7 @@ neon-build = "0.4.0"
 [dependencies]
 bincode = "1.2.1"
 bs58 = "0.3.0"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 dubp-wot = { path = "../../rust-libs/dubp-wot" }
 duniter-server = { path = "../../rust-libs/duniter-server" }
 flate2 = "1.0.16"
diff --git a/rust-bins/duniter-dbex/Cargo.toml b/rust-bins/duniter-dbex/Cargo.toml
index ec9a940846e2423dcd66bb12472cdd1e5197557a..23439503c80327d1db5ef8e5c0bdd6ac17339eb9 100644
--- a/rust-bins/duniter-dbex/Cargo.toml
+++ b/rust-bins/duniter-dbex/Cargo.toml
@@ -22,7 +22,7 @@ anyhow = "1.0.33"
 arrayvec = "0.5.1"
 comfy-table = "1.0.0"
 dirs = "3.0.1"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-dbs = { path = "../../rust-libs/duniter-dbs", default-features = false, features = ["explorer", "leveldb_backend", "sled_backend"] }
 duniter-dbs-write-ops = { path = "../../rust-libs/duniter-dbs-write-ops", default-features = false, features = ["explorer", "leveldb_backend", "sled_backend"] }
 duniter-gva = { path = "../../rust-libs/modules/gva" }
diff --git a/rust-libs/duniter-conf/Cargo.toml b/rust-libs/duniter-conf/Cargo.toml
index 86b76c3a50053786f372914b9edc5f679a79bc2d..860f3992b329b64a409fb67e9457f5356fc7266b 100644
--- a/rust-libs/duniter-conf/Cargo.toml
+++ b/rust-libs/duniter-conf/Cargo.toml
@@ -6,5 +6,5 @@ license = "AGPL-3.0"
 edition = "2018"
 
 [dependencies]
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 serde = { version = "1.0.105", features = ["derive"] }
diff --git a/rust-libs/duniter-dbs-read-ops/Cargo.toml b/rust-libs/duniter-dbs-read-ops/Cargo.toml
index 908c5339c73bd43396304308ddc871932ed3ae97..7a64e8b667842390324484c1d4cbe845e3959d4b 100644
--- a/rust-libs/duniter-dbs-read-ops/Cargo.toml
+++ b/rust-libs/duniter-dbs-read-ops/Cargo.toml
@@ -14,7 +14,7 @@ path = "src/lib.rs"
 [dependencies]
 anyhow = "1.0.34"
 duniter-dbs = { path = "../duniter-dbs" }
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 resiter = "0.4.0"
 
 [dev-dependencies]
diff --git a/rust-libs/duniter-dbs-write-ops/Cargo.toml b/rust-libs/duniter-dbs-write-ops/Cargo.toml
index 387bc8c97a597fc17be7f85dc642efabd9eec316..6696ec08afada6fcbd639898e714170355434b61 100644
--- a/rust-libs/duniter-dbs-write-ops/Cargo.toml
+++ b/rust-libs/duniter-dbs-write-ops/Cargo.toml
@@ -13,7 +13,7 @@ path = "src/lib.rs"
 
 [dependencies]
 chrono = "0.4.19"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-dbs = { path = "../duniter-dbs" }
 fast-threadpool = "0.2.2"
 log = "0.4.11"
@@ -22,6 +22,7 @@ resiter = "0.4.0"
 [dev-dependencies]
 anyhow = "1.0.34"
 duniter-dbs = { path = "../duniter-dbs", features = ["mem"] }
+maplit = "1.0.2"
 serde_json = "1.0.53"
 
 [features]
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc.rs b/rust-libs/duniter-dbs-write-ops/src/bc.rs
index 013700b96552773f1b020ebf40bef43d5b551d72..7d236d05a6f30e4a5e900b0624051ece9a4d563a 100644
--- a/rust-libs/duniter-dbs-write-ops/src/bc.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/bc.rs
@@ -55,6 +55,8 @@ pub fn apply_block<B: Backend>(
         bc_db.uds_write(),
         bc_db.uds_reval_write(),
         bc_db.uids_index_write(),
+        bc_db.utxos_write(),
+        bc_db.consumed_utxos_write(),
     )
         .write(
             |(
@@ -64,6 +66,8 @@ pub fn apply_block<B: Backend>(
                 mut uds,
                 mut uds_reval,
                 mut uids_index,
+                mut utxos,
+                mut consumed_utxos,
             )| {
                 blocks_meta.upsert(U32BE(block.number().0), block_meta);
                 identities::update_identities::<B>(&block, &mut identities)?;
@@ -81,14 +85,35 @@ pub fn apply_block<B: Backend>(
                         &mut uds_reval,
                     )?;
                 }
-                txs::apply_txs::<B>(block.transactions(), &mut txs_hashs, &mut uds)?;
+                txs::apply_txs::<B>(
+                    block.number(),
+                    block.transactions(),
+                    &mut txs_hashs,
+                    &mut uds,
+                    &mut utxos,
+                    &mut consumed_utxos,
+                )?;
                 Ok(())
             },
         )?;
 
+    if block_meta.number > ROLL_BACK_MAX {
+        prune_bc_db(bc_db, BlockNumber(block_meta.number))?;
+    }
+
     Ok(block_meta)
 }
 
+fn prune_bc_db<B: Backend>(
+    bc_db: &duniter_dbs::databases::bc_v2::BcV2Db<B>,
+    current_block_number: BlockNumber,
+) -> KvResult<()> {
+    bc_db
+        .consumed_utxos_write()
+        .remove(U32BE(current_block_number.0 - ROLL_BACK_MAX))?;
+    Ok(())
+}
+
 pub fn revert_block<B: Backend>(
     bc_db: &duniter_dbs::databases::bc_v2::BcV2Db<B>,
     block: &DubpBlockV10,
@@ -100,6 +125,8 @@ pub fn revert_block<B: Backend>(
         bc_db.uds_write(),
         bc_db.uds_reval_write(),
         bc_db.uids_index_write(),
+        bc_db.utxos_write(),
+        bc_db.consumed_utxos_write(),
     )
         .write(
             |(
@@ -109,8 +136,17 @@ pub fn revert_block<B: Backend>(
                 mut uds,
                 mut uds_reval,
                 mut uids_index,
+                mut utxos,
+                mut consumed_utxos,
             )| {
-                txs::revert_txs::<B>(block.transactions(), &mut txs_hashs, &mut uds)?;
+                txs::revert_txs::<B>(
+                    block.number(),
+                    block.transactions(),
+                    &mut txs_hashs,
+                    &mut uds,
+                    &mut utxos,
+                    &mut consumed_utxos,
+                )?;
                 if block.dividend().is_some() {
                     uds::revert_uds::<B>(
                         block.number(),
@@ -133,3 +169,177 @@ pub fn revert_block<B: Backend>(
             },
         )
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use dubp::{
+        crypto::keys::PublicKey as _, documents::transaction::TransactionDocumentV10Stringified,
+        documents_parser::prelude::FromStringObject,
+    };
+    use duniter_dbs::{
+        databases::bc_v2::*, BlockUtxosV2Db, UtxoIdDbV2, WalletScriptWithSourceAmountV1Db,
+    };
+    use maplit::hashmap;
+
+    #[test]
+    fn test_bc_apply_block() -> anyhow::Result<()> {
+        let bc_db = BcV2Db::<Mem>::open(MemConf::default())?;
+
+        let s1 = WalletScriptV10::single_sig(PublicKey::from_base58(
+            "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
+        )?);
+        let s2 = WalletScriptV10::single_sig(PublicKey::from_base58(
+            "4fHMTFBMo5sTQEc5p1CNWz28S4mnnqdUBmECq1zt4n2m",
+        )?);
+
+        let b0 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified {
+            version: 10,
+            median_time: 5_243,
+            dividend: Some(1000),
+            joiners: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()],
+            inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()),
+            signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),
+            hash: Some("0000000000000000000000000000000000000000000000000000000000000000".to_owned()),
+            ..Default::default()
+        })?;
+
+        apply_block(&bc_db, &b0)?;
+
+        assert_eq!(bc_db.blocks_meta().count()?, 1);
+        assert_eq!(bc_db.uds().count()?, 1);
+        assert_eq!(bc_db.utxos().count()?, 0);
+        assert_eq!(bc_db.consumed_utxos().count()?, 0);
+
+        let b1 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified {
+            number: 1,
+            version: 10,
+            median_time: 5_245,
+            transactions: vec![TransactionDocumentV10Stringified {
+                currency: "test".to_owned(),
+                blockstamp: "0-0000000000000000000000000000000000000000000000000000000000000000".to_owned(),
+                locktime: 0,
+                issuers: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx".to_owned()],
+                inputs: vec!["1000:0:D:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:0".to_owned()],
+                unlocks: vec![],
+                outputs: vec![
+                    "600:0:SIG(4fHMTFBMo5sTQEc5p1CNWz28S4mnnqdUBmECq1zt4n2m)".to_owned(),
+                    "400:0:SIG(D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx)".to_owned(),
+                ],
+                comment: "".to_owned(),
+                signatures: vec![],
+                hash: Some("0000000000000000000000000000000000000000000000000000000000000000".to_owned()),
+            }],
+            inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()),
+            signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),
+            hash: Some("0000000000000000000000000000000000000000000000000000000000000000".to_owned()),
+            ..Default::default()
+        })?;
+
+        apply_block(&bc_db, &b1)?;
+
+        assert_eq!(bc_db.blocks_meta().count()?, 2);
+        assert_eq!(bc_db.uds().count()?, 0);
+        assert_eq!(bc_db.utxos().count()?, 2);
+        assert_eq!(
+            bc_db
+                .utxos()
+                .iter(.., |it| it.collect::<KvResult<Vec<_>>>())?,
+            vec![
+                (
+                    UtxoIdDbV2(Hash::default(), 0),
+                    WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s2.clone(),
+                        source_amount: SourceAmount::with_base0(600)
+                    }
+                ),
+                (
+                    UtxoIdDbV2(Hash::default(), 1),
+                    WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s1.clone(),
+                        source_amount: SourceAmount::with_base0(400)
+                    }
+                )
+            ]
+        );
+        assert_eq!(bc_db.consumed_utxos().count()?, 0);
+
+        let b2 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified {
+            number: 2,
+            version: 10,
+            median_time: 5_247,
+            transactions: vec![TransactionDocumentV10Stringified {
+                currency: "test".to_owned(),
+                blockstamp: "0-0000000000000000000000000000000000000000000000000000000000000000".to_owned(),
+                locktime: 0,
+                issuers: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx".to_owned()],
+                inputs: vec!["400:0:T:0000000000000000000000000000000000000000000000000000000000000000:1".to_owned()],
+                unlocks: vec![],
+                outputs: vec![
+                    "300:0:SIG(D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx)".to_owned(),
+                    "100:0:SIG(4fHMTFBMo5sTQEc5p1CNWz28S4mnnqdUBmECq1zt4n2m)".to_owned(),
+                ],
+                comment: "".to_owned(),
+                signatures: vec![],
+                hash: Some("0101010101010101010101010101010101010101010101010101010101010101".to_owned()),
+            }],
+            inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()),
+            signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),
+            hash: Some("0000000000000000000000000000000000000000000000000000000000000000".to_owned()),
+            ..Default::default()
+        })?;
+
+        apply_block(&bc_db, &b2)?;
+
+        assert_eq!(bc_db.blocks_meta().count()?, 3);
+        assert_eq!(bc_db.uds().count()?, 0);
+        assert_eq!(bc_db.utxos().count()?, 3);
+        assert_eq!(bc_db.consumed_utxos().count()?, 1);
+
+        assert_eq!(
+            bc_db
+                .consumed_utxos()
+                .iter(.., |it| it.collect::<KvResult<Vec<_>>>())?,
+            vec![(
+                U32BE(2),
+                BlockUtxosV2Db(
+                    hashmap![UtxoIdV10 { tx_hash: Hash::default(), output_index: 1 } => WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s1.clone(),
+                        source_amount: SourceAmount::with_base0(400)
+                    }]
+                )
+            )]
+        );
+
+        assert_eq!(
+            bc_db
+                .utxos()
+                .iter(.., |it| it.collect::<KvResult<Vec<_>>>())?,
+            vec![
+                (
+                    UtxoIdDbV2(Hash::default(), 0),
+                    WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s2.clone(),
+                        source_amount: SourceAmount::with_base0(600)
+                    }
+                ),
+                (
+                    UtxoIdDbV2(Hash([1; 32]), 0),
+                    WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s1,
+                        source_amount: SourceAmount::with_base0(300)
+                    }
+                ),
+                (
+                    UtxoIdDbV2(Hash([1; 32]), 1),
+                    WalletScriptWithSourceAmountV1Db {
+                        wallet_script: s2,
+                        source_amount: SourceAmount::with_base0(100)
+                    }
+                )
+            ]
+        );
+
+        Ok(())
+    }
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
index 87b36acc78f41f6b5b240b0c22a7e9f89741d492..d8154fd0ca6f82f000096daaa0958d01601c4b6e 100644
--- a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
@@ -13,40 +13,105 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+use std::collections::HashMap;
+
 use crate::*;
+use dubp::documents::transaction::TransactionOutputV10;
 use duniter_dbs::{
-    databases::bc_v2::{TxsHashsEvent, UdsEvent},
-    UdIdV2,
+    databases::bc_v2::{ConsumedUtxosEvent, TxsHashsEvent, UdsEvent, UtxosEvent},
+    BlockUtxosV2Db, UdIdV2, UtxoIdDbV2, WalletScriptWithSourceAmountV1Db,
 };
 
 pub(crate) fn apply_txs<B: Backend>(
+    block_number: BlockNumber,
     block_txs: &[TransactionDocumentV10],
     txs_hashs: &mut TxColRw<B::Col, TxsHashsEvent>,
     uds: &mut TxColRw<B::Col, UdsEvent>,
+    utxos: &mut TxColRw<B::Col, UtxosEvent>,
+    consumed_utxos: &mut TxColRw<B::Col, ConsumedUtxosEvent>,
 ) -> KvResult<()> {
-    for tx in block_txs {
-        txs_hashs.upsert(HashKeyV2(tx.get_hash()), ());
-        for input in tx.get_inputs() {
-            if let SourceIdV10::Ud(UdSourceIdV10 {
-                issuer,
-                block_number,
-            }) = input.id
+    if !block_txs.is_empty() {
+        let mut block_consumed_utxos = HashMap::with_capacity(block_txs.len() * 3);
+        for tx in block_txs {
+            let tx_hash = tx.get_hash();
+            txs_hashs.upsert(HashKeyV2(tx_hash), ());
+            for input in tx.get_inputs() {
+                match input.id {
+                    SourceIdV10::Ud(UdSourceIdV10 {
+                        issuer,
+                        block_number,
+                    }) => {
+                        uds.remove(UdIdV2(issuer, block_number));
+                    }
+                    SourceIdV10::Utxo(utxo_id) => {
+                        let utxo_id_db = UtxoIdDbV2(utxo_id.tx_hash, utxo_id.output_index as u32);
+                        if let Some(wallet_script_with_sa) = utxos.get(&utxo_id_db)? {
+                            utxos.remove(utxo_id_db);
+                            block_consumed_utxos.insert(utxo_id, wallet_script_with_sa);
+                        } else {
+                            return Err(KvError::Custom(
+                                format!("db corrupted: not found utxo {:?}", utxo_id_db).into(),
+                            ));
+                        }
+                    }
+                }
+            }
+            for (output_index, TransactionOutputV10 { amount, conditions }) in
+                tx.get_outputs().iter().enumerate()
             {
-                uds.remove(UdIdV2(issuer, block_number));
+                let utxo_id = UtxoIdDbV2(tx_hash, output_index as u32);
+                let wallet_script_with_sa = WalletScriptWithSourceAmountV1Db {
+                    wallet_script: conditions.script.clone(),
+                    source_amount: *amount,
+                };
+                utxos.upsert(utxo_id, wallet_script_with_sa);
             }
         }
+        if !block_consumed_utxos.is_empty() {
+            consumed_utxos.upsert(U32BE(block_number.0), BlockUtxosV2Db(block_consumed_utxos));
+        }
     }
     Ok(())
 }
 
 pub(crate) fn revert_txs<B: Backend>(
+    block_number: BlockNumber,
     block_txs: &[TransactionDocumentV10],
     txs_hashs: &mut TxColRw<B::Col, TxsHashsEvent>,
     uds: &mut TxColRw<B::Col, UdsEvent>,
+    utxos: &mut TxColRw<B::Col, UtxosEvent>,
+    consumed_utxos: &mut TxColRw<B::Col, ConsumedUtxosEvent>,
 ) -> KvResult<()> {
     for tx in block_txs {
-        txs_hashs.remove(HashKeyV2(tx.get_hash()));
+        let tx_hash = tx.get_hash();
+        txs_hashs.remove(HashKeyV2(tx_hash));
         for input in tx.get_inputs() {
+            match input.id {
+                SourceIdV10::Ud(UdSourceIdV10 {
+                    issuer,
+                    block_number,
+                }) => {
+                    uds.upsert(UdIdV2(issuer, block_number), ());
+                }
+                SourceIdV10::Utxo(utxo_id) => {
+                    let utxo_id_db = UtxoIdDbV2(utxo_id.tx_hash, utxo_id.output_index as u32);
+                    if let Some(block_utxos) = consumed_utxos.get(&U32BE(block_number.0))? {
+                        if let Some(wallet_script_with_sa) = block_utxos.0.get(&utxo_id) {
+                            utxos.upsert(utxo_id_db, wallet_script_with_sa.clone());
+                        } else {
+                            return Err(KvError::Custom(
+                                format!("db corrupted: not found consumed utxos {}", utxo_id)
+                                    .into(),
+                            ));
+                        }
+                    } else {
+                        return Err(KvError::Custom(
+                            format!("db corrupted: not found consumed utxos {:?}", utxo_id_db)
+                                .into(),
+                        ));
+                    }
+                }
+            }
             if let SourceIdV10::Ud(UdSourceIdV10 {
                 issuer,
                 block_number,
@@ -55,6 +120,11 @@ pub(crate) fn revert_txs<B: Backend>(
                 uds.upsert(UdIdV2(issuer, block_number), ());
             }
         }
+        for output_index in 0..tx.get_outputs().len() {
+            let utxo_id = UtxoIdDbV2(tx_hash, output_index as u32);
+            utxos.remove(utxo_id);
+        }
     }
+    consumed_utxos.remove(U32BE(block_number.0));
     Ok(())
 }
diff --git a/rust-libs/duniter-dbs-write-ops/src/lib.rs b/rust-libs/duniter-dbs-write-ops/src/lib.rs
index 405a35a899dba4cabf3acff6d3d80e22a1528e4b..8ccb4d566cb6d897af151cf74009e832b62f955e 100644
--- a/rust-libs/duniter-dbs-write-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/lib.rs
@@ -49,3 +49,5 @@ use resiter::filter_map::FilterMap;
 use resiter::flatten::Flatten;
 use resiter::map::Map;
 use std::ops::Deref;
+
+const ROLL_BACK_MAX: u32 = 100;
diff --git a/rust-libs/duniter-dbs/Cargo.toml b/rust-libs/duniter-dbs/Cargo.toml
index 813fd38fd28b22e9e5c872cd4b137f079e10df61..33b35cdb03263bcc3d8ca2a7ca46996ad9386f79 100644
--- a/rust-libs/duniter-dbs/Cargo.toml
+++ b/rust-libs/duniter-dbs/Cargo.toml
@@ -16,7 +16,7 @@ arrayvec = "0.5.1"
 bincode = "1.2.1"
 byteorder = "1.3.4"
 chrono = { version = "0.4.15", optional = true }
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 kv_typed = { path = "../tools/kv_typed", default-features = false }
 log = "0.4.8"
 mockall = { version = "0.8.0", optional = true }
diff --git a/rust-libs/duniter-dbs/src/databases/bc_v2.rs b/rust-libs/duniter-dbs/src/databases/bc_v2.rs
index 81dae61f2981c47c1f07ab5d65d2097c90ea414a..c5256029671a892a8e1a676e32ccb85d7f44556a 100644
--- a/rust-libs/duniter-dbs/src/databases/bc_v2.rs
+++ b/rust-libs/duniter-dbs/src/databases/bc_v2.rs
@@ -24,5 +24,7 @@ db_schema!(
         ["uds", Uds, UdIdV2, ()],
         ["uds_reval", UdsReval, U32BE, SourceAmountValV2],
         ["uids_index", UidsIndex, String, PubKeyValV2],
+        ["utxos", Utxos, UtxoIdDbV2, WalletScriptWithSourceAmountV1Db],
+        ["consumed_utxos", ConsumedUtxos, U32BE, BlockUtxosV2Db],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs
index 6ac199e37260abd7cf6d6099fd38ef15b8930f90..480936e7a82d02fb0129d4b5ccb509f4fcc8dfda 100644
--- a/rust-libs/duniter-dbs/src/lib.rs
+++ b/rust-libs/duniter-dbs/src/lib.rs
@@ -83,9 +83,10 @@ pub use values::sindex_db::{SIndexDBV1, SourceKeyArrayDbV1};
 pub use values::source_amount::SourceAmountValV2;
 pub use values::tx_db::{PendingTxDbV2, TxDbV2};
 pub use values::ud_entry_db::{ConsumedUdDbV1, UdAmountDbV1, UdEntryDbV1};
-pub use values::utxo::UtxoValV2;
+pub use values::utxo::{BlockUtxosV2Db, UtxoValV2};
 pub use values::wallet_db::WalletDbV1;
 pub use values::wallet_script_array::WalletScriptArrayV2;
+pub use values::wallet_script_with_sa::WalletScriptWithSourceAmountV1Db;
 
 // Crate imports
 pub(crate) use arrayvec::{ArrayString, ArrayVec};
diff --git a/rust-libs/duniter-dbs/src/values.rs b/rust-libs/duniter-dbs/src/values.rs
index 27a5ce4fa70dc8404ee320022e08bcb26ba191bc..5471d4da42dbb260c30ebaceda70bbf30d9bec9a 100644
--- a/rust-libs/duniter-dbs/src/values.rs
+++ b/rust-libs/duniter-dbs/src/values.rs
@@ -33,3 +33,4 @@ pub mod ud_entry_db;
 pub mod utxo;
 pub mod wallet_db;
 pub mod wallet_script_array;
+pub mod wallet_script_with_sa;
diff --git a/rust-libs/duniter-dbs/src/values/utxo.rs b/rust-libs/duniter-dbs/src/values/utxo.rs
index f24ef57c2244bf06ff323e65a7b142f51448f2bb..bfbfd358b9e50d9a7420c21d1dd86071ff325329 100644
--- a/rust-libs/duniter-dbs/src/values/utxo.rs
+++ b/rust-libs/duniter-dbs/src/values/utxo.rs
@@ -14,7 +14,7 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 use crate::*;
-use std::ops::Deref;
+use std::{collections::HashMap, ops::Deref};
 
 #[derive(
     Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, zerocopy::AsBytes, zerocopy::FromBytes,
@@ -108,6 +108,39 @@ impl ExplorableValue for UtxoValV2 {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct BlockUtxosV2Db(pub HashMap<UtxoIdV10, WalletScriptWithSourceAmountV1Db>);
+
+impl ValueAsBytes for BlockUtxosV2Db {
+    fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+    }
+}
+
+impl kv_typed::prelude::FromBytes for BlockUtxosV2Db {
+    type Err = StringErr;
+
+    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
+        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+    }
+}
+
+impl ToDumpString for BlockUtxosV2Db {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableValue for BlockUtxosV2Db {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+        unimplemented!()
+    }
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.to_string()))?)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs b/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs
new file mode 100644
index 0000000000000000000000000000000000000000..def12e5059ab48cdbf67a11caa9364e5e5e2fc1f
--- /dev/null
+++ b/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs
@@ -0,0 +1,52 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use crate::*;
+
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct WalletScriptWithSourceAmountV1Db {
+    pub wallet_script: WalletScriptV10,
+    pub source_amount: SourceAmount,
+}
+
+impl ValueAsBytes for WalletScriptWithSourceAmountV1Db {
+    fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+    }
+}
+
+impl kv_typed::prelude::FromBytes for WalletScriptWithSourceAmountV1Db {
+    type Err = StringErr;
+
+    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
+        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+    }
+}
+
+impl ToDumpString for WalletScriptWithSourceAmountV1Db {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableValue for WalletScriptWithSourceAmountV1Db {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+        unimplemented!()
+    }
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.to_string()))?)
+    }
+}
diff --git a/rust-libs/duniter-mempools/Cargo.toml b/rust-libs/duniter-mempools/Cargo.toml
index c5bacfe9581594d5eaaacc788da0d0dd56248946..bd43adfc51b49ee213df3f6ef888abe5f132db66 100644
--- a/rust-libs/duniter-mempools/Cargo.toml
+++ b/rust-libs/duniter-mempools/Cargo.toml
@@ -12,7 +12,7 @@ edition = "2018"
 path = "src/lib.rs"
 
 [dependencies]
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-dbs = { path = "../duniter-dbs" }
 duniter-dbs-read-ops = { path = "../duniter-dbs-read-ops" }
 duniter-dbs-write-ops = { path = "../duniter-dbs-write-ops" }
diff --git a/rust-libs/duniter-module/Cargo.toml b/rust-libs/duniter-module/Cargo.toml
index a664eada89ac041861eb647ef6abfb166ff09e65..ae95637f8d9af7defc49a45a15b34e30d21cf960 100644
--- a/rust-libs/duniter-module/Cargo.toml
+++ b/rust-libs/duniter-module/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2018"
 [dependencies]
 anyhow = "1.0.34"
 async-trait = "0.1.41"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-conf = { path = "../duniter-conf" }
 duniter-dbs = { path = "../duniter-dbs" }
 duniter-mempools = { path = "../duniter-mempools" }
diff --git a/rust-libs/duniter-server/Cargo.toml b/rust-libs/duniter-server/Cargo.toml
index 408ceeb1a5ac77bf560ade73dbabeb924223ce26..4d8f22b7a8ec5beec618c14468e2ca080d34afb6 100644
--- a/rust-libs/duniter-server/Cargo.toml
+++ b/rust-libs/duniter-server/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2018"
 [dependencies]
 anyhow = "1.0.34"
 cfg-if = "1.0.0"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-conf = { path = "../duniter-conf" }
 duniter-dbs = { path = "../duniter-dbs" }
 duniter-dbs-read-ops = { path = "../duniter-dbs-read-ops" }
diff --git a/rust-libs/modules/gva/Cargo.toml b/rust-libs/modules/gva/Cargo.toml
index 965bf4c17511f88a9ae20625ae4de21b136586c3..5695a8cba0e3d18b9352e6ac4d304441dccedef4 100644
--- a/rust-libs/modules/gva/Cargo.toml
+++ b/rust-libs/modules/gva/Cargo.toml
@@ -11,7 +11,7 @@ arrayvec = "0.5.1"
 async-graphql = "2.0.0"
 async-mutex = "1.4.0"
 async-trait = "0.1.41"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-conf = { path = "../../duniter-conf" }
 duniter-dbs = { path = "../../duniter-dbs" }
 duniter-dbs-read-ops = { path = "../../duniter-dbs-read-ops" }
diff --git a/rust-libs/modules/gva/db-writer/Cargo.toml b/rust-libs/modules/gva/db-writer/Cargo.toml
index 424ae6146f438be7bb6740670e971510a886d3e5..801f2f15ba2534460de32e53870635094cdc4735 100644
--- a/rust-libs/modules/gva/db-writer/Cargo.toml
+++ b/rust-libs/modules/gva/db-writer/Cargo.toml
@@ -14,7 +14,7 @@ path = "src/lib.rs"
 [dependencies]
 anyhow = "1.0.34"
 duniter-dbs = { path = "../../../duniter-dbs" }
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 resiter = "0.4.0"
 
 [dev-dependencies]
diff --git a/rust-libs/modules/gva/dbs-reader/Cargo.toml b/rust-libs/modules/gva/dbs-reader/Cargo.toml
index 78d4956b29adf9fa0622de59be1dcc61941f4fed..e83081ca3fc54e80515f9d1eba9c456a876358b9 100644
--- a/rust-libs/modules/gva/dbs-reader/Cargo.toml
+++ b/rust-libs/modules/gva/dbs-reader/Cargo.toml
@@ -14,7 +14,7 @@ path = "src/lib.rs"
 [dependencies]
 anyhow = "1.0.34"
 duniter-dbs = { path = "../../../duniter-dbs" }
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 resiter = "0.4.0"
 
 [dev-dependencies]
diff --git a/rust-libs/tests/duniter-integration-tests/Cargo.toml b/rust-libs/tests/duniter-integration-tests/Cargo.toml
index 355a6a7aa53a9a558aff2928107c1dce567966c1..6016dd250d92aa73b94111298eb290867498ce02 100644
--- a/rust-libs/tests/duniter-integration-tests/Cargo.toml
+++ b/rust-libs/tests/duniter-integration-tests/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
 
 [dependencies]
 anyhow = "1.0.34"
-dubp = { version = "0.33.0" }
+dubp = { version = "0.34.0" }
 duniter-conf = { path = "../../duniter-conf" }
 duniter-dbs = { path = "../../duniter-dbs" }
 duniter-dbs-read-ops = { path = "../../duniter-dbs-read-ops" }