diff --git a/Cargo.lock b/Cargo.lock
index e73f5eb58a6472b91fe5cca496716d1bf50813f4..5af30e7cdd0fa35334e5ab256564929618bafd6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -69,9 +69,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
 
 [[package]]
 name = "arrayvec"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
 [[package]]
 name = "async-attributes"
@@ -110,9 +110,9 @@ dependencies = [
 
 [[package]]
 name = "async-global-executor"
-version = "1.4.0"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0659b83a146398616883aa5199cdd1b055ec088a83c9a338eab3534f33f0622e"
+checksum = "124ac8c265e407641c3362b8f4d39cdb4e243885b71eef087be27199790f5a3a"
 dependencies = [
  "async-executor",
  "async-io",
@@ -123,9 +123,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql"
-version = "2.0.4"
+version = "2.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a058ea006ad7e2f25b4594cdb5b60da29b28526e9579ef1b1adfad7d5a353d5"
+checksum = "6aacd1f33609c3f37792005ddfd9dd40891c6c4553a1d22cc290b3dc664c3ade"
 dependencies = [
  "async-graphql-derive",
  "async-graphql-parser",
@@ -161,9 +161,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-derive"
-version = "2.0.4"
+version = "2.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed048285ebf1e6e517b62e9cd6a313b941297e9ee56233534f63ac2e8c24abfd"
+checksum = "4865ce6f7993b9e06f8cab15ee16e083b7802fb681771510b6d18cc9eb70412c"
 dependencies = [
  "Inflector",
  "async-graphql-parser",
@@ -177,9 +177,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-parser"
-version = "2.0.4"
+version = "2.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98384ffe6801a3094428f43e799630ccf75edb23a1db6a26d5ee117f3613a9f0"
+checksum = "ca660e5dea2757fefec931f34c7babb01ff7eb01756494f66929cbb8411cf98a"
 dependencies = [
  "async-graphql-value",
  "pest",
@@ -190,9 +190,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-value"
-version = "2.0.4"
+version = "2.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b445404350572b3845165a41fd5b509364074ce8daf3dc1bb7867ce139930d26"
+checksum = "57d3aa3cd3696ffd8decb10f5053affc78cb33ecfc545e480072bbc600e6723d"
 dependencies = [
  "serde",
  "serde_json",
@@ -227,6 +227,15 @@ dependencies = [
  "event-listener",
 ]
 
+[[package]]
+name = "async-oneshot"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f4770cbbff928c30a991de67fb3976f44d8e3e202f8c79ef91b47006e04904"
+dependencies = [
+ "futures-micro",
+]
+
 [[package]]
 name = "async-std"
 version = "1.6.5"
@@ -238,7 +247,7 @@ dependencies = [
  "async-io",
  "async-mutex",
  "blocking",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -278,9 +287,9 @@ dependencies = [
 
 [[package]]
 name = "async-task"
-version = "4.0.2"
+version = "4.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ab27c1aa62945039e44edaeee1dc23c74cc0c303dd5fe0fb462a184f1c3a518"
+checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
 
 [[package]]
 name = "async-trait"
@@ -449,9 +458,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
+checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
 dependencies = [
  "lazy_static",
  "memchr",
@@ -513,6 +522,9 @@ name = "cc"
 version = "1.0.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
+dependencies = [
+ "jobserver",
+]
 
 [[package]]
 name = "cfg-if"
@@ -620,6 +632,12 @@ dependencies = [
  "cache-padded",
 ]
 
+[[package]]
+name = "const_fn"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
+
 [[package]]
 name = "constant_time_eq"
 version = "0.1.5"
@@ -634,11 +652,11 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
 
 [[package]]
 name = "crc32fast"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -679,23 +697,23 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.4"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
+checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
- "crossbeam-utils",
- "maybe-uninit",
+ "cfg-if 1.0.0",
+ "crossbeam-utils 0.8.0",
 ]
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.7.3"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
+checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
 dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
- "maybe-uninit",
+ "cfg-if 1.0.0",
+ "crossbeam-epoch 0.9.0",
+ "crossbeam-utils 0.8.0",
 ]
 
 [[package]]
@@ -706,13 +724,27 @@ checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
 dependencies = [
  "autocfg 1.0.1",
  "cfg-if 0.1.10",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
  "lazy_static",
  "maybe-uninit",
  "memoffset",
  "scopeguard",
 ]
 
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
+dependencies = [
+ "cfg-if 1.0.0",
+ "const_fn",
+ "crossbeam-utils 0.8.0",
+ "lazy_static",
+ "memoffset",
+ "scopeguard",
+]
+
 [[package]]
 name = "crossbeam-utils"
 version = "0.7.2"
@@ -724,6 +756,18 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
+dependencies = [
+ "autocfg 1.0.1",
+ "cfg-if 1.0.0",
+ "const_fn",
+ "lazy_static",
+]
+
 [[package]]
 name = "crossterm"
 version = "0.17.7"
@@ -734,7 +778,7 @@ dependencies = [
  "crossterm_winapi",
  "lazy_static",
  "libc",
- "mio 0.7.3",
+ "mio 0.7.4",
  "parking_lot 0.10.2",
  "signal-hook",
  "winapi 0.3.9",
@@ -742,9 +786,9 @@ dependencies = [
 
 [[package]]
 name = "crossterm_winapi"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
+checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db"
 dependencies = [
  "winapi 0.3.9",
 ]
@@ -915,7 +959,7 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
 [[package]]
 name = "dubp"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "dubp-block",
  "dubp-common",
@@ -928,7 +972,7 @@ dependencies = [
 [[package]]
 name = "dubp-block"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "dubp-documents",
  "dubp-documents-parser",
@@ -942,7 +986,7 @@ dependencies = [
 [[package]]
 name = "dubp-common"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "dup-crypto",
  "serde",
@@ -954,7 +998,7 @@ dependencies = [
 [[package]]
 name = "dubp-documents"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "beef",
  "dubp-wallet",
@@ -967,7 +1011,7 @@ dependencies = [
 [[package]]
 name = "dubp-documents-parser"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "dubp-documents",
  "json-pest-parser",
@@ -980,7 +1024,7 @@ dependencies = [
 [[package]]
 name = "dubp-wallet"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "byteorder",
  "dubp-common",
@@ -1005,11 +1049,14 @@ dependencies = [
 name = "duniter-dbex"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "arrayvec",
  "comfy-table",
  "dirs",
  "dubp",
  "duniter-dbs",
+ "duniter-dbs-write-ops",
+ "fast-threadpool",
  "rayon",
  "serde",
  "serde_json",
@@ -1030,11 +1077,13 @@ dependencies = [
  "log",
  "mockall",
  "once_cell",
+ "rand 0.7.3",
  "serde",
  "serde_json",
  "smallvec",
  "tempdir",
  "thiserror",
+ "uninit",
  "unwrap",
  "zerocopy",
 ]
@@ -1054,6 +1103,7 @@ dependencies = [
  "chrono",
  "dubp",
  "duniter-dbs",
+ "fast-threadpool",
  "log",
  "resiter",
  "serde_json",
@@ -1121,7 +1171,6 @@ dependencies = [
  "fast-threadpool",
  "flume",
  "log",
- "rand 0.7.3",
  "resiter",
  "unwrap",
 ]
@@ -1150,7 +1199,7 @@ dependencies = [
 [[package]]
 name = "dup-crypto"
 version = "0.29.0"
-source = "git+https://git.duniter.org/libs/dubp-rs-libs#7d397ec510fc8225ea2b563493b36eecc6b5caa9"
+source = "git+https://git.duniter.org/libs/dubp-rs-libs#b3b2322240cfcc8af17dc223677dd19c8b20c746"
 dependencies = [
  "base64",
  "bs58",
@@ -1213,13 +1262,13 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
 
 [[package]]
 name = "fast-threadpool"
-version = "0.1.1"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e3f69f217417e35f8f2c52256c8852dcd72e1933261195d437d5bfc14245793"
+checksum = "cf9f76ead92b1c1e372f552dcd2e331fb321d6b16886df58ed7586208e211e14"
 dependencies = [
+ "async-oneshot",
  "flume",
  "num_cpus",
- "oneshot",
 ]
 
 [[package]]
@@ -1339,9 +1388,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 
 [[package]]
 name = "futures"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e"
+checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1354,9 +1403,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
+checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1364,15 +1413,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
+checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab"
+checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1381,15 +1430,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c"
+checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
 
 [[package]]
 name = "futures-lite"
-version = "1.11.1"
+version = "1.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "381a7ad57b1bad34693f63f6f377e1abded7a9c85c9d3eb6771e11c60aaadab9"
+checksum = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658"
 dependencies = [
  "fastrand",
  "futures-core",
@@ -1402,9 +1451,9 @@ dependencies = [
 
 [[package]]
 name = "futures-macro"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b"
+checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -1412,26 +1461,32 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "futures-micro"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61e9325be55c5581082cd110294fa988c1f920bc573ec370ef201e33c469a95a"
+
 [[package]]
 name = "futures-sink"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
+checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
 
 [[package]]
 name = "futures-task"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
+checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
 dependencies = [
  "once_cell",
 ]
 
 [[package]]
 name = "futures-util"
-version = "0.3.6"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
+checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1440,7 +1495,7 @@ dependencies = [
  "futures-sink",
  "futures-task",
  "memchr",
- "pin-project",
+ "pin-project 1.0.1",
  "pin-utils",
  "proc-macro-hack",
  "proc-macro-nested",
@@ -1462,19 +1517,6 @@ version = "0.3.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
 
-[[package]]
-name = "generator"
-version = "0.6.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc"
-dependencies = [
- "cc",
- "libc",
- "log",
- "rustc_version",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "generic-array"
 version = "0.12.3"
@@ -1542,9 +1584,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
+checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
 dependencies = [
  "bytes",
  "fnv",
@@ -1557,6 +1599,7 @@ dependencies = [
  "tokio",
  "tokio-util",
  "tracing",
+ "tracing-futures",
 ]
 
 [[package]]
@@ -1679,7 +1722,7 @@ dependencies = [
  "httparse",
  "httpdate",
  "itoa",
- "pin-project",
+ "pin-project 0.4.27",
  "socket2",
  "tokio",
  "tower-service",
@@ -1725,11 +1768,11 @@ dependencies = [
 
 [[package]]
 name = "instant"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
+checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -1756,6 +1799,15 @@ version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
 
+[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "js-sys"
 version = "0.3.45"
@@ -1862,9 +1914,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
+checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 
 [[package]]
 name = "liblmdb-sys"
@@ -1927,17 +1979,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d0925aed5b12ed59857f438d25a910cf051dbcd4107907be1e7abf6c44ec903"
 
-[[package]]
-name = "loom"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed"
-dependencies = [
- "cfg-if 0.1.10",
- "generator",
- "scoped-tls",
-]
-
 [[package]]
 name = "lru"
 version = "0.6.0"
@@ -1961,9 +2002,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
 
 [[package]]
 name = "maybe-async"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27790c036ea5ec16a39435631eb60449ba2a83ee056b989f8dcd36a2e5c0c5fb"
+checksum = "fd1afac51d14f8056cd544c83239b961c464e0a98c2ca65353195df93e636a20"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2038,9 +2079,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e53a6ea5f38c0a48ca42159868c6d8e1bd56c0451238856cc08d58563643bdc3"
+checksum = "f8f1c83949125de4a582aa2da15ae6324d91cf6a58a70ea407643941ff98f558"
 dependencies = [
  "libc",
  "log",
@@ -2073,9 +2114,9 @@ dependencies = [
 
 [[package]]
 name = "mockall"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf52bd480d59ec342893c9c64ace644b4bbb1e184f8217312f0282107a372e4d"
+checksum = "41cabea45a7fc0e37093f4f30a5e2b62602253f91791c057d5f0470c63260c3d"
 dependencies = [
  "cfg-if 0.1.10",
  "downcast",
@@ -2088,9 +2129,9 @@ dependencies = [
 
 [[package]]
 name = "mockall_derive"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f060c7e8d81fa8cf7bfd4a2cc183402d1066c9cba56998e2807b109d8c0ec"
+checksum = "7c461918bf7f59eefb1459252756bf2351a995d6bd510d0b2061bd86bcdabfa6"
 dependencies = [
  "cfg-if 0.1.10",
  "proc-macro2",
@@ -2250,9 +2291,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
 
 [[package]]
 name = "ntapi"
-version = "0.3.4"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2"
+checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
 dependencies = [
  "winapi 0.3.9",
 ]
@@ -2343,15 +2384,6 @@ version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
 
-[[package]]
-name = "oneshot"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39d7085e4e51b36df4afa83db60d20ad2adf8e8587a193f93c9143bf7b375dec"
-dependencies = [
- "loom",
-]
-
 [[package]]
 name = "oorandom"
 version = "11.1.2"
@@ -2496,7 +2528,16 @@ version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
 dependencies = [
- "pin-project-internal",
+ "pin-project-internal 0.4.27",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841"
+dependencies = [
+ "pin-project-internal 1.0.1",
 ]
 
 [[package]]
@@ -2510,11 +2551,22 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "pin-project-internal"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "pin-project-lite"
-version = "0.1.10"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95"
+checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
 
 [[package]]
 name = "pin-utils"
@@ -2536,9 +2588,9 @@ dependencies = [
 
 [[package]]
 name = "polling"
-version = "2.0.1"
+version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab773feb154f12c49ffcfd66ab8bdcf9a1843f950db48b0d8be9d4393783b058"
+checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
@@ -2813,9 +2865,9 @@ dependencies = [
 
 [[package]]
 name = "rayon"
-version = "1.4.1"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032"
+checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
 dependencies = [
  "autocfg 1.0.1",
  "crossbeam-deque",
@@ -2825,13 +2877,13 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.8.1"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
+checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
- "crossbeam-utils",
+ "crossbeam-utils 0.8.0",
  "lazy_static",
  "num_cpus",
 ]
@@ -2928,14 +2980,14 @@ dependencies = [
  "base64",
  "blake2b_simd",
  "constant_time_eq",
- "crossbeam-utils",
+ "crossbeam-utils 0.7.2",
 ]
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.17"
+version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b"
+checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
 
 [[package]]
 name = "rustc_version"
@@ -3104,7 +3156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
 dependencies = [
  "libc",
- "mio 0.7.3",
+ "mio 0.7.4",
  "signal-hook-registry",
 ]
 
@@ -3131,13 +3183,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f72c064e63fbca3138ad07f3588c58093f1684f3a99f60dcfa6d46b87e60fde7"
 dependencies = [
  "crc32fast",
- "crossbeam-epoch",
- "crossbeam-utils",
+ "crossbeam-epoch 0.8.2",
+ "crossbeam-utils 0.7.2",
  "fs2",
  "fxhash",
  "libc",
  "log",
  "parking_lot 0.11.0",
+ "zstd",
 ]
 
 [[package]]
@@ -3271,9 +3324,9 @@ checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63"
 
 [[package]]
 name = "syn"
-version = "1.0.44"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
+checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3407,7 +3460,7 @@ checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c"
 dependencies = [
  "futures-util",
  "log",
- "pin-project",
+ "pin-project 0.4.27",
  "tokio",
  "tungstenite",
 ]
@@ -3480,7 +3533,7 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
 dependencies = [
- "pin-project",
+ "pin-project 0.4.27",
  "tracing",
 ]
 
@@ -3714,7 +3767,7 @@ dependencies = [
  "mime",
  "mime_guess",
  "multipart",
- "pin-project",
+ "pin-project 0.4.27",
  "scoped-tls",
  "serde",
  "serde_json",
@@ -3817,9 +3870,9 @@ dependencies = [
 
 [[package]]
 name = "wepoll-sys"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "142bc2cba3fe88be1a8fcb55c727fa4cd5b0cf2d7438722792e22f26f04bc1e0"
+checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
 dependencies = [
  "cc",
 ]
@@ -3926,3 +3979,34 @@ dependencies = [
  "syn",
  "synstructure",
 ]
+
+[[package]]
+name = "zstd"
+version = "0.5.3+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "2.0.5+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "1.4.17+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
+dependencies = [
+ "cc",
+ "glob",
+ "itertools",
+ "libc",
+]
diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts
index a6105de9c502e1c589359d0b7559f8678b1ee16e..17e113f94b23b81f6d57a94ad22f488d6b616e0a 100644
--- a/app/lib/blockchain/DuniterBlockchain.ts
+++ b/app/lib/blockchain/DuniterBlockchain.ts
@@ -327,9 +327,6 @@ export class DuniterBlockchain {
     // Saves the block (DAL)
     await dal.saveBlock(dbb, conf);
 
-    // Send block to rust server
-    dal.rustServer.applyBlock(block);
-
     // Save wot file
     if (!dal.fs.isMemoryOnly()) {
       const wotbFilepath = Directory.getWotbFilePath(dal.rootPath);
@@ -496,8 +493,8 @@ export class DuniterBlockchain {
     dal: FileDAL,
     block?: DBBlock
   ) {
-    if (block && block.toBlockDTO) {
-      dal.rustServer.revertBlock(block.toBlockDTO());
+    if (block) {
+      dal.rustServer.revertBlock(BlockDTO.fromJSONObject(block));
     }
 
     const blockstamp = [number, hash].join("-");
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index 57122d88c5161fa7b7981ff3a380a53c8e84ce27..7334c653cab9bb2099ca9c5b874bba97918723de 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -192,9 +192,9 @@ export class FileDAL implements ServerDAO {
     };
   }
 
-  async init(conf: ConfDTO) {
+  async init(conf: ConfDTO, commandName: string | null = null) {
     // Rust server
-    this.initRustServer(conf);
+    this.initRustServer(conf, commandName);
 
     // wotb
     this.wotb = this.params.wotbf();
@@ -232,9 +232,10 @@ export class FileDAL implements ServerDAO {
     }
   }
 
-  initRustServer(conf: ConfDTO) {
+  initRustServer(conf: ConfDTO, commandName: string | null = null) {
     let serverPubkey = conf.pair ? conf.pair.pub : null;
     let rustServerConf = {
+      command: commandName,
       gva: conf.gva,
       serverPubkey,
       txsMempoolSize:
@@ -1241,8 +1242,12 @@ export class FileDAL implements ServerDAO {
 
   async saveBlock(block: DBBlock, conf: ConfDTO) {
     block.wrong = false;
-    this.rustServer.applyBlock(block.toBlockDTO());
-    await this.saveBlockInFile(block);
+    try {
+      this.rustServer.applyBlock(block.toBlockDTO());
+      await this.saveBlockInFile(block);
+    } catch (err) {
+      throw err;
+    }
   }
 
   async generateIndexes(
diff --git a/index.ts b/index.ts
index c0a4b0623db978716e9971ed4a6126297a067129..f7dd1363ab6c0235c833230f192bdde6971a087c 100644
--- a/index.ts
+++ b/index.ts
@@ -397,7 +397,7 @@ export class Stack {
         throw `Command ${command.name} does not implement onConfiguredExecute nor onDatabaseExecute.`
       }
       // Second possible class of commands: post-service
-      await server.initDAL(conf);
+      await server.initDAL(conf, command.name);
 
       /**
        * Service injection
diff --git a/neon/native/server.d.ts b/neon/native/server.d.ts
index 0f6bb45ffd55884fa25e46e8d900f7ae0ffc8b28..e17a0bfd852ce9ffa5d7e1490d3b604487c22d39 100644
--- a/neon/native/server.d.ts
+++ b/neon/native/server.d.ts
@@ -3,6 +3,7 @@
 import { TransactionDTOV10 } from './transaction';
 
 export class RustServerConf {
+    command: string | null
     gva: GvaConf | undefined
     serverPubkey: string | null
     txsMempoolSize: number
diff --git a/neon/native/src/server.rs b/neon/native/src/server.rs
index 6e4e860574b11227b1486da56836d15cbc4f37b5..017578783a965a8aa53aee2ba961c55043c0f24d 100644
--- a/neon/native/src/server.rs
+++ b/neon/native/src/server.rs
@@ -51,6 +51,7 @@ declare_types! {
             } else {
                 None
             };
+            let command_name = rust_server_conf_stringified.command_name;
             let server_pubkey = if let Some(server_pubkey_str) = rust_server_conf_stringified.server_pubkey {
                 into_neon_res(&mut cx, PublicKey::from_base58(&server_pubkey_str))?
             } else {
@@ -79,10 +80,10 @@ declare_types! {
                 None
             };
             if let Some(home_path) = home_path_opt {
-                let server = DuniterServer::start(conf, Some(home_path.as_path()), std::env!("CARGO_PKG_VERSION"));
+                let server = DuniterServer::start(command_name, conf, Some(home_path.as_path()), std::env!("CARGO_PKG_VERSION"));
                 Ok(RustServer { server })
             } else {
-                let server = DuniterServer::start(conf, None, std::env!("CARGO_PKG_VERSION"));
+                let server = DuniterServer::start(command_name, conf, None, std::env!("CARGO_PKG_VERSION"));
                 Ok(RustServer { server })
             }
         }
@@ -144,10 +145,10 @@ declare_types! {
 
             let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?;
 
-            let this = cx.this();
+            let mut this = cx.this();
             let res = {
                 let guard = cx.lock();
-                let server = this.borrow(&guard);
+                let mut server = this.borrow_mut(&guard);
                 server.server.revert_block(block_stringified)
             }.map(|()| cx.undefined().upcast());
             into_neon_res(&mut cx, res)
@@ -201,10 +202,10 @@ declare_types! {
 
             let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?;
 
-            let this = cx.this();
+            let mut this = cx.this();
             let res = {
                 let guard = cx.lock();
-                let server = this.borrow(&guard);
+                let mut server = this.borrow_mut(&guard);
                 server.server.apply_block(block_stringified)
             }.map(|()| cx.undefined().upcast());
             into_neon_res(&mut cx, res)
@@ -214,10 +215,10 @@ declare_types! {
 
             let blocks_stringified: Vec<dubp::block::DubpBlockV10Stringified> = neon_serde::from_value(&mut cx, blocks_js)?;
 
-            let this = cx.this();
+            let mut this = cx.this();
             let res = {
                 let guard = cx.lock();
-                let server = this.borrow(&guard);
+                let mut server = this.borrow_mut(&guard);
                 server.server.apply_chunk_of_blocks(blocks_stringified)
             }.map(|()| cx.undefined().upcast());
             into_neon_res(&mut cx, res)
@@ -293,6 +294,7 @@ declare_types! {
 #[derive(Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 struct RustServerConfStringified {
+    command_name: Option<String>,
     gva: Option<GvaConfStringified>,
     server_pubkey: Option<String>,
     txs_mempool_size: u32,
diff --git a/rust-bins/duniter-dbex/Cargo.toml b/rust-bins/duniter-dbex/Cargo.toml
index a62fcacba5049a4bb506d480a41d58c3f5cd52d5..f491939c9c4313cef8527a2d75d29712c354370e 100644
--- a/rust-bins/duniter-dbex/Cargo.toml
+++ b/rust-bins/duniter-dbex/Cargo.toml
@@ -18,11 +18,14 @@ name = "dex"
 structopt = "0.3.16"
 
 [dependencies]
+anyhow = "1.0.33"
 arrayvec = "0.5.1"
 comfy-table = "1.0.0"
 dirs = "3.0.1"
 dubp = { version = "0.29.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"] }
+fast-threadpool = "0.2.1"
 rayon = "1.3.1"
 serde_json = "1.0.53"
 structopt = "0.3.16"
diff --git a/rust-bins/duniter-dbex/README.md b/rust-bins/duniter-dbex/README.md
index f49c48fd89510f6efa24c7b495396cac545d7d21..99d1ab982d24dad51125d5304f882acda329c085 100644
--- a/rust-bins/duniter-dbex/README.md
+++ b/rust-bins/duniter-dbex/README.md
@@ -20,6 +20,6 @@ Bash autocompletion script is available here : `target/release/dex.bash`
 
 To generate the autocompletion script for your shell, recompile with env var `COMPLETION_SHELL`.
 
-For exemple for fish : `COMPLETION_SHELL=fish cargo build --release -p duniter-dbex`
+For example for fish : `COMPLETION_SHELL=fish cargo build --release -p duniter-dbex`
 
 The autocompletion script can be found in : `target/release/`
diff --git a/rust-bins/duniter-dbex/src/cli.rs b/rust-bins/duniter-dbex/src/cli.rs
index 1544fe20ded8da981e3c2ce10e7337efa28faae2..e98ee25e2c44f06f620797a52bd275be8e8d92bf 100644
--- a/rust-bins/duniter-dbex/src/cli.rs
+++ b/rust-bins/duniter-dbex/src/cli.rs
@@ -28,7 +28,7 @@ pub struct Opt {
     pub home: Option<PathBuf>,
 
     /// database
-    #[structopt(default_value = "bc_v1", possible_values = &["bc_v1", "gva_v1", "txs_mp_v2"])]
+    #[structopt(default_value = "bc_v1", possible_values = &["bc_v1", "bc_v2", "gva_v1", "txs_mp_v2"])]
     pub database: Database,
 
     #[structopt(subcommand)]
@@ -38,6 +38,7 @@ pub struct Opt {
 #[derive(Debug)]
 pub enum Database {
     BcV1,
+    BcV2,
     GvaV1,
     TxsMpV2,
 }
@@ -48,6 +49,7 @@ impl FromStr for Database {
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "bc_v1" => Ok(Self::BcV1),
+            "bc_v2" => Ok(Self::BcV2),
             "gva_v1" => Ok(Self::GvaV1),
             "txs_mp_v2" => Ok(Self::TxsMpV2),
             _ => unreachable!(),
@@ -103,6 +105,8 @@ pub enum SubCommand {
     },
     /// Show database schema
     Schema,
+    /// Fill rust dbs from js db content
+    Migrate,
 }
 
 #[derive(Clone, Copy, Debug)]
diff --git a/rust-bins/duniter-dbex/src/main.rs b/rust-bins/duniter-dbex/src/main.rs
index cf655c5c94b3c6255151f20909daf38076103882..f203b5ddda95b571e1ac54d7e004c7d67771e6e4 100644
--- a/rust-bins/duniter-dbex/src/main.rs
+++ b/rust-bins/duniter-dbex/src/main.rs
@@ -23,12 +23,15 @@
 )]
 
 mod cli;
+mod migrate;
 mod print_found_data;
 mod stringify_json_value;
 
 use self::cli::{Database, Opt, OutputFormat, SubCommand};
 use self::stringify_json_value::stringify_json_value;
+use anyhow::anyhow;
 use comfy_table::Table;
+use duniter_dbs::bc_v2::{BcV2Db, BcV2DbWritable};
 use duniter_dbs::kv_typed::backend::sled;
 use duniter_dbs::kv_typed::prelude::*;
 use duniter_dbs::prelude::*;
@@ -50,7 +53,7 @@ use structopt::StructOpt;
 const DATA_DIR: &str = "data";
 const TOO_MANY_ENTRIES_ALERT: usize = 5_000;
 
-fn main() -> Result<(), String> {
+fn main() -> anyhow::Result<()> {
     let opt = Opt::from_args();
 
     let home = if let Some(home) = opt.home {
@@ -58,8 +61,7 @@ fn main() -> Result<(), String> {
     } else {
         dirs::config_dir()
             .ok_or_else(|| {
-                "Fail to auto find duniter's home directory, please specify it explicitly."
-                    .to_owned()
+                anyhow!("Fail to auto find duniter's home directory, please specify it explicitly.")
             })?
             .as_path()
             .join("duniter")
@@ -75,39 +77,47 @@ fn main() -> Result<(), String> {
     let data_path = profile_path.as_path().join(DATA_DIR);
 
     if !data_path.exists() {
-        return Err(format!(
+        return Err(anyhow!(
             "Path '{}' don't exist !",
             data_path.to_str().expect("non-UTF-8 strings not supported")
         ));
     }
 
-    let open_db_start_time = Instant::now();
-    match opt.database {
-        Database::BcV1 => apply_subcommand(
-            BcV1Db::<LevelDb>::open(LevelDbConf {
-                db_path: data_path.as_path().join("leveldb"),
-                ..Default::default()
-            })
-            .map_err(|e| format!("{}", e))?,
-            opt.cmd,
-            open_db_start_time,
-        ),
-        Database::GvaV1 => apply_subcommand(
-            GvaV1Db::<Sled>::open(
-                sled::Config::default().path(data_path.as_path().join("gva_v1_sled")),
-            )
-            .map_err(|e| format!("{}", e))?,
-            opt.cmd,
-            open_db_start_time,
-        ),
-        Database::TxsMpV2 => apply_subcommand(
-            TxsMpV2Db::<Sled>::open(
-                sled::Config::default().path(data_path.as_path().join("txs_mp_v2_sled")),
-            )
-            .map_err(|e| format!("{}", e))?,
-            opt.cmd,
-            open_db_start_time,
-        ),
+    if let SubCommand::Migrate = opt.cmd {
+        migrate::migrate(profile_path)
+    } else {
+        let open_db_start_time = Instant::now();
+        match opt.database {
+            Database::BcV1 => apply_subcommand(
+                BcV1Db::<LevelDb>::open(LevelDbConf {
+                    db_path: data_path.as_path().join("leveldb"),
+                    ..Default::default()
+                })?,
+                opt.cmd,
+                open_db_start_time,
+            ),
+            Database::BcV2 => apply_subcommand(
+                BcV2Db::<Sled>::open(
+                    sled::Config::default().path(data_path.as_path().join("bc_v2_sled")),
+                )?,
+                opt.cmd,
+                open_db_start_time,
+            ),
+            Database::GvaV1 => apply_subcommand(
+                GvaV1Db::<Sled>::open(
+                    sled::Config::default().path(data_path.as_path().join("gva_v1_sled")),
+                )?,
+                opt.cmd,
+                open_db_start_time,
+            ),
+            Database::TxsMpV2 => apply_subcommand(
+                TxsMpV2Db::<Sled>::open(
+                    sled::Config::default().path(data_path.as_path().join("txs_mp_v2_sled")),
+                )?,
+                opt.cmd,
+                open_db_start_time,
+            ),
+        }
     }
 }
 
@@ -115,7 +125,7 @@ fn apply_subcommand<DB: DbExplorable>(
     db: DB,
     cmd: SubCommand,
     open_db_start_time: Instant,
-) -> Result<(), String> {
+) -> anyhow::Result<()> {
     let duration = open_db_start_time.elapsed();
     println!(
         "Database opened in {}.{:06} seconds.",
@@ -126,10 +136,8 @@ fn apply_subcommand<DB: DbExplorable>(
 
     match cmd {
         SubCommand::Count { collection } => {
-            if let ExplorerActionResponse::Count(count) = db
-                .explore(&collection, ExplorerAction::Count, stringify_json_value)
-                .map_err(|e| format!("{}", e))?
-                .map_err(|e| e.0)?
+            if let ExplorerActionResponse::Count(count) =
+                db.explore(&collection, ExplorerAction::Count, stringify_json_value)??
             {
                 let duration = start_time.elapsed();
                 println!(
@@ -141,15 +149,11 @@ fn apply_subcommand<DB: DbExplorable>(
             }
         }
         SubCommand::Get { collection, key } => {
-            if let ExplorerActionResponse::Get(value_opt) = db
-                .explore(
-                    &collection,
-                    ExplorerAction::Get { key: &key },
-                    stringify_json_value,
-                )
-                .map_err(|e| format!("{}", e))?
-                .map_err(|e| e.0)?
-            {
+            if let ExplorerActionResponse::Get(value_opt) = db.explore(
+                &collection,
+                ExplorerAction::Get { key: &key },
+                stringify_json_value,
+            )?? {
                 if let Some(value) = value_opt {
                     println!("\n{}", value)
                 } else {
@@ -189,23 +193,19 @@ fn apply_subcommand<DB: DbExplorable>(
             } else {
                 vec![]
             };
-            if let ExplorerActionResponse::Find(entries) = db
-                .explore(
-                    &collection,
-                    ExplorerAction::Find {
-                        key_min: start,
-                        key_max: end,
-                        key_regex: opt_string_to_res_opt_regex(key_regex)?,
-                        value_regex: value_regex_opt,
-                        limit,
-                        reverse,
-                        step,
-                    },
-                    stringify_json_value,
-                )
-                .map_err(|e| format!("{}", e))?
-                .map_err(|e| e.0)?
-            {
+            if let ExplorerActionResponse::Find(entries) = db.explore(
+                &collection,
+                ExplorerAction::Find {
+                    key_min: start,
+                    key_max: end,
+                    key_regex: opt_string_to_res_opt_regex(key_regex)?,
+                    value_regex: value_regex_opt,
+                    limit,
+                    reverse,
+                    step,
+                },
+                stringify_json_value,
+            )?? {
                 let duration = start_time.elapsed();
                 println!(
                     "Search performed in {}.{:06} seconds.\n\n{} entries found.",
@@ -214,16 +214,13 @@ fn apply_subcommand<DB: DbExplorable>(
                     entries.len()
                 );
 
-                if !too_many_entries(entries.len(), output_file.is_none())
-                    .map_err(|e| format!("{}", e))?
-                {
+                if !too_many_entries(entries.len(), output_file.is_none())? {
                     return Ok(());
                 }
 
                 let start_print = Instant::now();
                 if let Some(output_file) = output_file {
-                    let mut file =
-                        File::create(output_file.as_path()).map_err(|e| format!("{}", e))?;
+                    let mut file = File::create(output_file.as_path())?;
 
                     //let mut file_buffer = BufWriter::new(file);
                     print_found_data::print_found_data(
@@ -237,8 +234,7 @@ fn apply_subcommand<DB: DbExplorable>(
                             only_properties: properties,
                         },
                         captures_headers,
-                    )
-                    .map_err(|e| format!("{}", e))?;
+                    )?;
                     //file_buffer.flush().map_err(|e| format!("{}", e))?;
 
                     let export_duration = start_print.elapsed();
@@ -262,8 +258,7 @@ fn apply_subcommand<DB: DbExplorable>(
                             only_properties: properties,
                         },
                         captures_headers,
-                    )
-                    .map_err(|e| format!("{}", e))?;
+                    )?;
                     let print_duration = start_print.elapsed();
                     println!(
                         "Search results were displayed in {}.{:06} seconds.",
@@ -276,6 +271,7 @@ fn apply_subcommand<DB: DbExplorable>(
         SubCommand::Schema => {
             show_db_schema(db.list_collections());
         }
+        SubCommand::Migrate => unreachable!(),
     };
 
     Ok(())
@@ -313,9 +309,9 @@ fn show_db_schema(collections_names: Vec<(&'static str, &'static str, &'static s
 }
 
 #[inline]
-fn opt_string_to_res_opt_regex(str_regex_opt: Option<String>) -> Result<Option<Regex>, String> {
+fn opt_string_to_res_opt_regex(str_regex_opt: Option<String>) -> anyhow::Result<Option<Regex>> {
     if let Some(str_regex) = str_regex_opt {
-        Ok(Some(Regex::new(&str_regex).map_err(|e| format!("{}", e))?))
+        Ok(Some(Regex::new(&str_regex)?))
     } else {
         Ok(None)
     }
diff --git a/rust-bins/duniter-dbex/src/migrate.rs b/rust-bins/duniter-dbex/src/migrate.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3146ed0c7e772a622dfdec12868b201c03048c24
--- /dev/null
+++ b/rust-bins/duniter-dbex/src/migrate.rs
@@ -0,0 +1,157 @@
+//  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::*;
+use dubp::{
+    block::parser::parse_json_block_from_serde_value, block::parser::ParseJsonBlockError,
+    block::prelude::DubpBlockTrait, block::DubpBlock, common::prelude::BlockNumber,
+};
+use duniter_dbs::BcV1DbReadable;
+use fast_threadpool::{ThreadPool, ThreadPoolConfig};
+use std::path::PathBuf;
+
+const CHUNK_SIZE: usize = 250;
+
+pub(crate) fn migrate(profile_path: PathBuf) -> anyhow::Result<()> {
+    let start_time = Instant::now();
+    let dbs = duniter_dbs::open_dbs(Some(profile_path.as_path()));
+
+    let data_path = profile_path.join(crate::DATA_DIR);
+    let duniter_js_db = BcV1Db::<LevelDb>::open(LevelDbConf {
+        db_path: data_path.as_path().join("leveldb"),
+        ..Default::default()
+    })?;
+
+    let dbs_pool = ThreadPool::start(ThreadPoolConfig::default(), dbs).into_sync_handler();
+
+    if let Some(target) = get_target_block_number(&duniter_js_db)? {
+        println!("target block: #{}", target.0);
+        /*let chunk_count = if (target.0 as usize + 1) % CHUNK_SIZE == 0 {
+            (target.0 as usize + 1) / CHUNK_SIZE
+        } else {
+            (target.0 as usize / CHUNK_SIZE) + 1
+        };*/
+
+        let (s, r) = std::sync::mpsc::channel();
+        let reader_handle = std::thread::spawn(move || {
+            duniter_js_db.main_blocks().iter(.., |it| {
+                it.values()
+                    .map(|block_res| s.send(block_res).map_err(|_| anyhow!("fail to send")))
+                    .collect::<anyhow::Result<()>>()
+            })
+        });
+        let (s2, r2) = std::sync::mpsc::channel();
+        let parser_handle = std::thread::spawn(move || {
+            let target_u64 = target.0 as u64;
+            let mut db_blocks = Vec::with_capacity(CHUNK_SIZE);
+            while let Ok(db_block_res) = r.recv() {
+                let db_block = db_block_res?;
+                let db_block_number = db_block.number;
+                db_blocks.push(db_block);
+                if db_blocks.len() == CHUNK_SIZE || db_block_number == target_u64 {
+                    let blocks = std::mem::take(&mut db_blocks)
+                        .into_par_iter()
+                        .map(|db_block| match serde_json::to_value(&db_block) {
+                            Ok(json_block) => {
+                                match parse_json_block_from_serde_value(&json_block) {
+                                    Ok(block) => match block {
+                                        DubpBlock::V10(block_v10) => Ok(block_v10),
+                                    },
+                                    Err(e) => Err(anyhow::Error::new::<ParseJsonBlockError>(e)),
+                                }
+                            }
+                            Err(e) => Err(anyhow::Error::new::<serde_json::Error>(e)),
+                        })
+                        .collect::<anyhow::Result<Vec<_>>>()?;
+                    s2.send(blocks).map_err(|_| anyhow!("fail to send"))?;
+                    db_blocks.reserve_exact(CHUNK_SIZE);
+                }
+            }
+            Ok::<(), anyhow::Error>(())
+        });
+
+        let mut current = None;
+        while let Ok(chunk) = r2.recv() {
+            if !chunk.is_empty() {
+                println!(
+                    "Apply chunk #{}-#{} ..",
+                    chunk[0].number(),
+                    chunk[chunk.len() - 1].number()
+                );
+                current = Some(duniter_dbs_write_ops::apply_block::apply_chunk(
+                    current, &dbs_pool, chunk, true,
+                )?);
+            }
+        }
+
+        reader_handle.join().expect("reader thread panic")?;
+        parser_handle.join().expect("parser thread panic")?;
+
+        dbs_pool.execute(|dbs| {
+            dbs.bc_db.save()?;
+            dbs.gva_db.save()?;
+            Ok::<(), KvError>(())
+        })??;
+
+        if let Some(current) = current {
+            if current.number != target.0 {
+                Err(anyhow::anyhow!("Migration fail: current != target"))
+            } else {
+                let duration = start_time.elapsed();
+                println!(
+                    "Migration successfully completed on {} seconds.",
+                    duration.as_secs()
+                );
+                Ok(())
+            }
+        } else {
+            Err(anyhow::anyhow!("Migration fail: rust dbs are empty"))
+        }
+    } else {
+        Err(anyhow::anyhow!("Empty blockchain"))
+    }
+}
+
+fn get_target_block_number(duniter_js_db: &BcV1Db<LevelDb>) -> KvResult<Option<BlockNumber>> {
+    duniter_js_db.main_blocks().iter(.., |it| {
+        it.reverse()
+            .keys()
+            .map(|k_res| k_res.map(|bn| bn.0))
+            .next_res()
+    })
+}
+
+/*fn get_chunk(duniter_js_db: &BcV1Db<LevelDb>, i: u32) -> anyhow::Result<Vec<DubpBlockV10>> {
+    let start = BlockNumberKeyV1(BlockNumber(i * CHUNK_SIZE));
+    let end = BlockNumberKeyV1(BlockNumber(((i + 1) * CHUNK_SIZE) - 1));
+    println!("get_chunk({}): range {}..{}", i, start.0, end.0);
+    let db_blocks = duniter_js_db
+        .main_blocks()
+        .iter(start..=end, |it| it.values().collect::<KvResult<Vec<_>>>())?;
+
+    db_blocks
+        .into_par_iter()
+        .map(|db_block| match serde_json::to_value(&db_block) {
+            Ok(json_block) => match parse_json_block_from_serde_value(&json_block) {
+                Ok(block) => match block {
+                    DubpBlock::V10(block_v10) => Ok(block_v10),
+                },
+                Err(e) => Err(anyhow::Error::new::<ParseJsonBlockError>(e)),
+            },
+            Err(e) => Err(anyhow::Error::new::<serde_json::Error>(e)),
+        })
+        .collect()
+}
+*/
diff --git a/rust-libs/duniter-dbs-read-ops/src/lib.rs b/rust-libs/duniter-dbs-read-ops/src/lib.rs
index c5bce22f260fc26aea9e8e989edda907a9ebbbf9..1fd64539477fcfe75f5acf4c3d7071f9f9484fc4 100644
--- a/rust-libs/duniter-dbs-read-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-read-ops/src/lib.rs
@@ -28,8 +28,11 @@ pub mod utxos;
 use dubp::common::crypto::hashs::Hash;
 use dubp::common::crypto::keys::ed25519::PublicKey;
 use dubp::documents::transaction::TransactionDocumentV10;
+use duniter_dbs::bc_v2::BcV2DbReadable;
 use duniter_dbs::{
     kv_typed::prelude::*,
+    BlockMetaV2, //TxsMpV2DbWritable,
+    //WalletConditionsV2
     //BlockNumberArrayV2, BlockNumberKeyV2, SourceAmountValV2, UtxosOfScriptV1
     //GvaV1Db,
     GvaV1DbReadable,
@@ -40,6 +43,10 @@ use duniter_dbs::{
     TxDbV2,
     //TxsMpV2Db,
     TxsMpV2DbReadable,
-    //TxsMpV2DbWritable,
-    //WalletConditionsV2,
 };
+
+pub fn get_current_block_meta<BcDb: BcV2DbReadable>(bc_db: &BcDb) -> KvResult<Option<BlockMetaV2>> {
+    bc_db
+        .blocks_meta()
+        .iter(.., |it| it.reverse().values().next_res())
+}
diff --git a/rust-libs/duniter-dbs-write-ops/Cargo.toml b/rust-libs/duniter-dbs-write-ops/Cargo.toml
index c6013efb36f0bf1fae8967c391b8537e3740b80c..f78eb6dc660428588c9cca9e3cda6383b1758c05 100644
--- a/rust-libs/duniter-dbs-write-ops/Cargo.toml
+++ b/rust-libs/duniter-dbs-write-ops/Cargo.toml
@@ -15,8 +15,16 @@ path = "src/lib.rs"
 chrono = "0.4.19"
 dubp = { version = "0.29.0" }
 duniter-dbs = { path = "../duniter-dbs" }
+fast-threadpool = "0.2.1"
 log = "0.4.11"
 resiter = "0.4.0"
 
 [dev-dependencies]
 serde_json = "1.0.53"
+
+[features]
+default = ["sled_backend"]
+
+explorer = ["duniter-dbs/explorer"]
+leveldb_backend = ["duniter-dbs/leveldb_backend"]
+sled_backend = ["duniter-dbs/sled_backend"]
diff --git a/rust-libs/duniter-dbs-write-ops/src/apply_block.rs b/rust-libs/duniter-dbs-write-ops/src/apply_block.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9e78303c896d53fb6d9f901fe31320164df4d001
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/apply_block.rs
@@ -0,0 +1,183 @@
+//  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::*;
+
+pub fn apply_block(
+    block: DubpBlockV10,
+    current_opt: Option<BlockMetaV2>,
+    dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
+    gva: bool,
+    throw_chainability: bool,
+) -> KvResult<BlockMetaV2> {
+    if let Some(current) = current_opt {
+        if block.number().0 == current.number + 1 {
+            apply_block_inner(dbs_pool, Arc::new(block), gva)
+        } else if throw_chainability {
+            Err(KvError::Custom(
+                format!(
+                    "block #{} not chainable on current #{}",
+                    block.number().0,
+                    current.number
+                )
+                .into(),
+            ))
+        } else {
+            Ok(current)
+        }
+    } else if block.number() == BlockNumber(0) {
+        apply_block_inner(dbs_pool, Arc::new(block), gva)
+    } else {
+        Err(KvError::Custom(
+            "Try to apply non genesis block on empty blockchain".into(),
+        ))
+    }
+}
+
+#[inline(always)]
+pub fn apply_chunk(
+    current_opt: Option<BlockMetaV2>,
+    dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
+    blocks: Vec<DubpBlockV10>,
+    gva: bool,
+) -> KvResult<BlockMetaV2> {
+    verify_chunk_chainability(current_opt, &blocks)?;
+    apply_chunk_inner(dbs_pool, Arc::new(blocks), gva)
+}
+
+fn verify_chunk_chainability(
+    current_opt: Option<BlockMetaV2>,
+    blocks: &[DubpBlockV10],
+) -> KvResult<()> {
+    if let Some(mut current) = current_opt {
+        for block in blocks {
+            if block.number().0 == current.number + 1 {
+                current.number += 1;
+            } else {
+                return Err(KvError::Custom(
+                    format!(
+                        "block #{} not chainable on current #{}",
+                        blocks[0].number().0,
+                        current.number
+                    )
+                    .into(),
+                ));
+            }
+        }
+        Ok(())
+    } else if blocks[0].number() == BlockNumber(0) {
+        let mut current_number = 0;
+        for block in &blocks[1..] {
+            if block.number().0 == current_number + 1 {
+                current_number += 1;
+            } else {
+                return Err(KvError::Custom(
+                    format!(
+                        "block #{} not chainable on current #{}",
+                        block.number().0,
+                        current_number
+                    )
+                    .into(),
+                ));
+            }
+        }
+        Ok(())
+    } else {
+        Err(KvError::Custom(
+            "Try to apply non genesis block on empty blockchain".into(),
+        ))
+    }
+}
+
+fn apply_block_inner(
+    dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
+    block: Arc<DubpBlockV10>,
+    gva: bool,
+) -> KvResult<BlockMetaV2> {
+    // Bc
+    let block_arc = Arc::clone(&block);
+    let bc_recv = dbs_pool
+        .launch(move |dbs| crate::bc::apply_block(&dbs.bc_db, &block_arc))
+        .expect("dbs pool disconnected");
+    //TxsMp
+    let block_arc = Arc::clone(&block);
+    let txs_mp_recv = dbs_pool
+        .launch(move |dbs| {
+            crate::txs_mp::apply_block(block_arc.transactions(), &dbs.txs_mp_db)?;
+            Ok::<_, KvError>(())
+        })
+        .expect("dbs pool disconnected");
+    // Gva
+    if gva {
+        let block_arc = Arc::clone(&block);
+        dbs_pool
+            .execute(move |dbs| {
+                crate::gva::apply_block(&block_arc, &dbs.gva_db)?;
+                Ok::<_, KvError>(())
+            })
+            .expect("dbs pool disconnected")?;
+    }
+    txs_mp_recv.join().expect("dbs pool disconnected")?;
+    bc_recv.join().expect("dbs pool disconnected")
+}
+
+fn apply_chunk_inner(
+    dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
+    blocks: Arc<Vec<DubpBlockV10>>,
+    gva: bool,
+) -> KvResult<BlockMetaV2> {
+    // Bc
+    let blocks_len = blocks.len();
+    let blocks_arc = Arc::clone(&blocks);
+    //log::info!("apply_chunk: launch bc job...");
+    let bc_handle = dbs_pool
+        .launch(move |dbs| {
+            for block in &blocks_arc[..(blocks_len - 1)] {
+                crate::bc::apply_block(&dbs.bc_db, block)?;
+            }
+            crate::bc::apply_block(&dbs.bc_db, &blocks_arc[blocks_len - 1])
+        })
+        .expect("apply_chunk_inner:bc: dbs pool disconnected");
+    //TxsMp
+    let blocks_arc = Arc::clone(&blocks);
+    //log::info!("apply_chunk: launch txs_mp job...");
+    let txs_mp_handle = dbs_pool
+        .launch(move |dbs| {
+            for block in blocks_arc.deref() {
+                crate::txs_mp::apply_block(block.transactions(), &dbs.txs_mp_db)?;
+            }
+            Ok::<_, KvError>(())
+        })
+        .expect("apply_chunk_inner:txs_mp: dbs pool disconnected");
+    // Gva
+    if gva {
+        let blocks_arc = Arc::clone(&blocks);
+        //log::info!("apply_chunk: launch gva job...");
+        dbs_pool
+            .execute(move |dbs| {
+                for block in blocks_arc.deref() {
+                    crate::gva::apply_block(&block, &dbs.gva_db)?;
+                }
+                Ok::<_, KvError>(())
+            })
+            .expect("apply_chunk_inner:gva: dbs pool disconnected")?;
+        //log::info!("apply_chunk: gva job finish.");
+    }
+    txs_mp_handle
+        .join()
+        .expect("txs_mp_recv: dbs pool disconnected")?;
+    //log::info!("apply_chunk: txs_mp job finish.");
+    bc_handle.join().expect("bc_recv: dbs pool disconnected")
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc.rs b/rust-libs/duniter-dbs-write-ops/src/bc.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3898eaff1ddcb93db11d0cb424faba70cfd8f864
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/bc.rs
@@ -0,0 +1,104 @@
+//  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/>.
+
+mod identities;
+mod txs;
+mod uds;
+
+use crate::*;
+use duniter_dbs::bc_v2::BcV2DbWritable;
+
+// ["blocks_meta", blocks_meta, BlockNumberKeyV2, BlockMetaV2,],
+// ["identities", identities, PubKeyKeyV2, IdtyDbV2,],
+
+pub fn revert_block<B: Backend>(
+    bc_db: &duniter_dbs::bc_v2::BcV2Db<B>,
+    block: DubpBlockV10,
+) -> KvResult<Option<BlockMetaV2>> {
+    (
+        bc_db.blocks_meta_write(),
+        bc_db.identities_write(),
+        bc_db.uds_write(),
+        bc_db.uds_reval_write(),
+    )
+        .write(
+            |(mut blocks_meta, mut identities, mut uds, mut uds_reval)| {
+                txs::revert_txs::<B>(block.transactions(), &mut uds)?;
+                if block.dividend().is_some() {
+                    uds::revert_uds::<B>(
+                        block.number(),
+                        &mut identities,
+                        &mut uds,
+                        &mut uds_reval,
+                    )?;
+                }
+                identities::revert_identities::<B>(&block, &mut identities)?;
+                blocks_meta.remove(BlockNumberKeyV2(block.number()));
+                Ok(if block.number() == BlockNumber(0) {
+                    None
+                } else {
+                    blocks_meta.get(&BlockNumberKeyV2(BlockNumber(block.number().0 - 1)))?
+                })
+            },
+        )
+}
+
+pub fn apply_block<B: Backend>(
+    bc_db: &duniter_dbs::bc_v2::BcV2Db<B>,
+    block: &DubpBlockV10,
+) -> KvResult<BlockMetaV2> {
+    //log::info!("apply_block #{}", block.number().0);
+    let block_meta = BlockMetaV2 {
+        version: 10,
+        number: block.number().0,
+        hash: block.hash().0,
+        issuer: block.issuer(),
+        signature: block.signature(),
+        inner_hash: block.inner_hash(),
+        previous_hash: block.previous_hash(),
+        pow_min: block.pow_min() as u32,
+        members_count: block.members_count() as u64,
+        issuers_count: block.issuers_count() as u32,
+        median_time: block.common_time(),
+        dividend: block.dividend(),
+        ..Default::default()
+    };
+
+    (
+        bc_db.blocks_meta_write(),
+        bc_db.identities_write(),
+        bc_db.uds_write(),
+        bc_db.uds_reval_write(),
+    )
+        .write(
+            |(mut blocks_meta, mut identities, mut uds, mut uds_reval)| {
+                blocks_meta.upsert(BlockNumberKeyV2(block.number()), block_meta);
+                identities::update_identities::<B>(&block, &mut identities)?;
+                if let Some(dividend) = block.dividend() {
+                    uds::create_uds::<B>(
+                        block.number(),
+                        dividend,
+                        &mut identities,
+                        &mut uds,
+                        &mut uds_reval,
+                    )?;
+                }
+                txs::apply_txs::<B>(block.transactions(), &mut uds)?;
+                Ok(())
+            },
+        )?;
+
+    Ok(block_meta)
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ed42c5014413bae6f5b1d2d291c2e835216e1f49
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs
@@ -0,0 +1,95 @@
+//  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::*;
+use duniter_dbs::bc_v2::IdentityEvent;
+use duniter_dbs::IdtyDbV2;
+
+pub(crate) fn update_identities<B: Backend>(
+    block: &DubpBlockV10,
+    identities: &mut TxColRw<B::Col, IdentityEvent>,
+) -> KvResult<()> {
+    for idty in block.identities() {
+        let pubkey = idty.issuers()[0];
+        let username = idty.username().to_owned();
+        identities.upsert(
+            PubKeyKeyV2(pubkey),
+            IdtyDbV2 {
+                member: true,
+                username,
+            },
+        )
+    }
+    for mb in block.joiners() {
+        let pubkey = mb.issuers()[0];
+        let username = mb.identity_username().to_owned();
+        identities.upsert(
+            PubKeyKeyV2(pubkey),
+            IdtyDbV2 {
+                member: true,
+                username,
+            },
+        )
+    }
+    for revo in block.revoked() {
+        let pubkey = revo.issuer;
+        if let Some(mut idty) = identities.get(&PubKeyKeyV2(pubkey))? {
+            idty.member = false;
+            identities.upsert(PubKeyKeyV2(pubkey), idty)
+        }
+    }
+    for pubkey in block.excluded().iter().copied() {
+        if let Some(mut idty) = identities.get(&PubKeyKeyV2(pubkey))? {
+            idty.member = false;
+            identities.upsert(PubKeyKeyV2(pubkey), idty)
+        }
+    }
+    Ok(())
+}
+
+pub(crate) fn revert_identities<B: Backend>(
+    block: &DubpBlockV10,
+    identities: &mut TxColRw<B::Col, IdentityEvent>,
+) -> KvResult<()> {
+    for mb in block.joiners() {
+        let pubkey = mb.issuers()[0];
+        let username = mb.identity_username().to_owned();
+        identities.upsert(
+            PubKeyKeyV2(pubkey),
+            IdtyDbV2 {
+                member: false,
+                username,
+            },
+        )
+    }
+    for idty in block.identities() {
+        let pubkey = idty.issuers()[0];
+        identities.remove(PubKeyKeyV2(pubkey));
+    }
+    for revo in block.revoked() {
+        let pubkey = revo.issuer;
+        if let Some(mut idty) = identities.get(&PubKeyKeyV2(pubkey))? {
+            idty.member = true;
+            identities.upsert(PubKeyKeyV2(pubkey), idty)
+        }
+    }
+    for pubkey in block.excluded().iter().copied() {
+        if let Some(mut idty) = identities.get(&PubKeyKeyV2(pubkey))? {
+            idty.member = true;
+            identities.upsert(PubKeyKeyV2(pubkey), idty)
+        }
+    }
+    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
new file mode 100644
index 0000000000000000000000000000000000000000..5e4b35a9533f1b08bfe989fc22b8090b14515809
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
@@ -0,0 +1,53 @@
+//  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::*;
+use duniter_dbs::{bc_v2::UdEvent, UdIdV2};
+
+pub(crate) fn apply_txs<B: Backend>(
+    block_txs: &[TransactionDocumentV10],
+    uds: &mut TxColRw<B::Col, UdEvent>,
+) -> KvResult<()> {
+    for tx in block_txs {
+        for input in tx.get_inputs() {
+            if let SourceIdV10::Ud(UdSourceIdV10 {
+                issuer,
+                block_number,
+            }) = input.id
+            {
+                uds.remove(UdIdV2(issuer, block_number));
+            }
+        }
+    }
+    Ok(())
+}
+
+pub(crate) fn revert_txs<B: Backend>(
+    block_txs: &[TransactionDocumentV10],
+    uds: &mut TxColRw<B::Col, UdEvent>,
+) -> KvResult<()> {
+    for tx in block_txs {
+        for input in tx.get_inputs() {
+            if let SourceIdV10::Ud(UdSourceIdV10 {
+                issuer,
+                block_number,
+            }) = input.id
+            {
+                uds.upsert(UdIdV2(issuer, block_number), EmptyValue);
+            }
+        }
+    }
+    Ok(())
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs
new file mode 100644
index 0000000000000000000000000000000000000000..73952aa2b3c7c9e1c068028fb248fca8995ef202
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs
@@ -0,0 +1,72 @@
+//  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::*;
+use duniter_dbs::{
+    bc_v2::{IdentityEvent, UdEvent, UdsRevalEvent},
+    UdIdV2,
+};
+
+// ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,],
+// ["uds", uds, UdIdV2, EmptyValue,],
+
+pub(crate) fn create_uds<B: Backend>(
+    block_number: BlockNumber,
+    dividend: SourceAmount,
+    identities: &mut TxColRw<B::Col, IdentityEvent>,
+    uds: &mut TxColRw<B::Col, UdEvent>,
+    uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>,
+) -> KvResult<()> {
+    let previous_ud_amount = uds_reval
+        .iter(.., |it| it.reverse().values().next_res())?
+        .unwrap_or_else(|| SourceAmountValV2(SourceAmount::new(0, 0)));
+    if dividend > previous_ud_amount.0 {
+        uds_reval.upsert(BlockNumberKeyV2(block_number), SourceAmountValV2(dividend));
+    }
+
+    let members = identities.iter(.., |it| {
+        it.filter_map_ok(|(pk, idty)| if idty.member { Some(pk.0) } else { None })
+            .collect::<KvResult<Vec<_>>>()
+    })?;
+    for member in members {
+        uds.upsert(UdIdV2(member, block_number), EmptyValue);
+    }
+    Ok(())
+}
+
+pub(crate) fn revert_uds<B: Backend>(
+    block_number: BlockNumber,
+    identities: &mut TxColRw<B::Col, IdentityEvent>,
+    uds: &mut TxColRw<B::Col, UdEvent>,
+    uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>,
+) -> KvResult<()> {
+    let previous_reval_block_number = uds_reval
+        .iter(.., |it| it.reverse().keys().next_res())?
+        .expect("corrupted db")
+        .0;
+    if block_number == previous_reval_block_number {
+        uds_reval.remove(BlockNumberKeyV2(block_number));
+    }
+
+    let members = identities.iter(.., |it| {
+        it.filter_map_ok(|(pk, idty)| if idty.member { Some(pk.0) } else { None })
+            .collect::<KvResult<Vec<_>>>()
+    })?;
+    for member in members {
+        uds.remove(UdIdV2(member, block_number));
+    }
+
+    Ok(())
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/gva.rs b/rust-libs/duniter-dbs-write-ops/src/gva.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bacd738c9035613e076e3736f49c9320e26c14e2
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/gva.rs
@@ -0,0 +1,70 @@
+//  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/>.
+
+mod tx;
+mod utxos;
+
+use crate::*;
+
+pub fn revert_block<B: Backend>(block: &DubpBlockV10, gva_db: &GvaV1Db<B>) -> KvResult<()> {
+    for idty in block.identities() {
+        let username = idty.username().to_owned();
+        gva_db.uids_index_write().remove(username)?;
+    }
+    for tx in block.transactions() {
+        let tx_hash = tx.get_hash();
+        tx::revert_tx(gva_db, &tx_hash)?.ok_or_else(|| {
+            KvError::DbCorrupted(format!("GVA: tx '{}' dont exist on txs history.", tx_hash,))
+        })?;
+    }
+
+    Ok(())
+}
+
+pub fn apply_block<B: Backend>(block: &DubpBlockV10, gva_db: &GvaV1Db<B>) -> KvResult<()> {
+    let blockstamp = Blockstamp {
+        number: block.number(),
+        hash: block.hash(),
+    };
+    for idty in block.identities() {
+        let pubkey = idty.issuers()[0];
+        let username = idty.username().to_owned();
+        gva_db
+            .uids_index_write()
+            .upsert(username, PubKeyValV2(pubkey))?;
+    }
+    write_block_txs(
+        &gva_db,
+        blockstamp,
+        block.common_time() as i64,
+        block.transactions(),
+    )?;
+
+    Ok(())
+}
+
+fn write_block_txs<B: Backend>(
+    gva_db: &GvaV1Db<B>,
+    current_blockstamp: Blockstamp,
+    current_time: i64,
+    txs: &[TransactionDocumentV10],
+) -> KvResult<()> {
+    for tx in txs {
+        let tx_hash = tx.get_hash();
+        // Write tx and update sources
+        tx::write_gva_tx(current_blockstamp, current_time, &gva_db, tx_hash, tx)?;
+    }
+    Ok(())
+}
diff --git a/rust-libs/duniter-dbs-write-ops/src/tx.rs b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
similarity index 96%
rename from rust-libs/duniter-dbs-write-ops/src/tx.rs
rename to rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
index 6dc3d21caa073c69ee15868654a0db2a0898bcb1..dbdad922ffe65a1b7fa574d1cb1fbf8a6214569f 100644
--- a/rust-libs/duniter-dbs-write-ops/src/tx.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
@@ -20,7 +20,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
     current_time: i64,
     gva_db: &GvaV1Db<B>,
     tx_hash: Hash,
-    tx: TransactionDocumentV10,
+    tx: &TransactionDocumentV10,
 ) -> KvResult<()> {
     (
         gva_db.scripts_by_pubkey_write(),
@@ -69,7 +69,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
                             .conditions
                             .script
                             .clone();
-                        utxos::remove_utxo_v10::<B>(
+                        super::utxos::remove_utxo_v10::<B>(
                             &mut scripts_by_pubkey,
                             &mut utxos_by_script,
                             &utxo_script,
@@ -80,7 +80,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
 
                 // Insert created UTXOs
                 for (output_index, output) in tx.get_outputs().iter().enumerate() {
-                    utxos::write_utxo_v10::<B>(
+                    super::utxos::write_utxo_v10::<B>(
                         &mut scripts_by_pubkey,
                         &mut utxos_by_script,
                         UtxoV10 {
@@ -99,7 +99,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
                 txs.upsert(
                     HashKeyV2(tx_hash),
                     TxDbV2 {
-                        tx,
+                        tx: tx.clone(),
                         written_block: current_blockstamp,
                         written_time: current_time,
                     },
@@ -138,7 +138,7 @@ pub(crate) fn revert_tx<B: Backend>(
                     use dubp::documents::transaction::TransactionDocumentTrait as _;
                     for output in tx_db.tx.get_outputs() {
                         let script = &output.conditions.script;
-                        utxos::remove_utxo_v10::<B>(
+                        super::utxos::remove_utxo_v10::<B>(
                             &mut scripts_by_pubkey,
                             &mut utxos_by_script,
                             script,
@@ -162,7 +162,7 @@ pub(crate) fn revert_tx<B: Backend>(
                                 .conditions
                                 .script
                                 .clone();
-                            utxos::write_utxo_v10::<B>(
+                            super::utxos::write_utxo_v10::<B>(
                                 &mut scripts_by_pubkey,
                                 &mut utxos_by_script,
                                 UtxoV10 {
diff --git a/rust-libs/duniter-dbs-write-ops/src/utxos.rs b/rust-libs/duniter-dbs-write-ops/src/gva/utxos.rs
similarity index 95%
rename from rust-libs/duniter-dbs-write-ops/src/utxos.rs
rename to rust-libs/duniter-dbs-write-ops/src/gva/utxos.rs
index 869d6d9cf16dd6591150d0ac4c502f1ee8936f2a..c38e43a3f9c8aa7a407cf63ca09f7298ba1055d7 100644
--- a/rust-libs/duniter-dbs-write-ops/src/utxos.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/gva/utxos.rs
@@ -15,13 +15,6 @@
 
 use crate::*;
 
-pub struct UtxoV10 {
-    pub id: UtxoIdV10,
-    pub amount: SourceAmount,
-    pub script: WalletScriptV10,
-    pub written_time: i64,
-}
-
 pub(crate) fn write_utxo_v10<B: Backend>(
     scripts_by_pubkey: &mut TxColRw<B::Col, duniter_dbs::gva_v1::ScriptsByPubkeyEvent>,
     utxos_by_script: &mut TxColRw<B::Col, duniter_dbs::gva_v1::UtxosByScriptEvent>,
diff --git a/rust-libs/duniter-dbs-write-ops/src/lib.rs b/rust-libs/duniter-dbs-write-ops/src/lib.rs
index 043295c09236a51adcd101e3295961f93d102194..42026eab3ae931ff56796bfe61ff1cb09253bf54 100644
--- a/rust-libs/duniter-dbs-write-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/lib.rs
@@ -22,266 +22,46 @@
     unused_import_braces
 )]
 
-mod identities;
-mod tx;
-mod utxos;
+pub mod apply_block;
+pub mod bc;
+pub mod gva;
+pub mod txs_mp;
 
 use std::borrow::Cow;
 
-use crate::utxos::UtxoV10;
-use dubp::block::DubpBlockV10Stringified;
+use dubp::block::prelude::*;
 use dubp::common::crypto::hashs::Hash;
 use dubp::common::prelude::*;
 use dubp::documents::{
     prelude::*, smallvec::SmallVec, transaction::TransactionDocumentTrait,
     transaction::TransactionDocumentV10,
 };
-use dubp::documents_parser::prelude::*;
 use dubp::wallet::prelude::*;
 use duniter_dbs::gva_v1::{TxEvent, TxsByIssuerEvent, TxsByRecipientEvent};
 use duniter_dbs::{
-    kv_typed::prelude::*, GvaV1Db, GvaV1DbReadable, GvaV1DbWritable, HashKeyV2, PendingTxDbV2,
-    PubKeyKeyV2, TxDbV2, TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbWritable, WalletConditionsV2,
+    kv_typed::prelude::*, BlockMetaV2, BlockNumberKeyV2, DuniterDbs, GvaV1Db, GvaV1DbReadable,
+    GvaV1DbWritable, HashKeyV2, PendingTxDbV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, TxDbV2,
+    TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbWritable, WalletConditionsV2,
 };
+use resiter::filter_map::FilterMap;
 use resiter::flatten::Flatten;
 use resiter::map::Map;
+use std::ops::Deref;
 
-pub fn add_pending_tx<
-    B: Backend,
-    F: FnOnce(
-        &TransactionDocumentV10,
-        &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxEvent>,
-    ) -> KvResult<()>,
->(
-    control: F,
-    txs_mp_db: &TxsMpV2Db<B>,
-    tx: Cow<TransactionDocumentV10>,
-) -> KvResult<()> {
-    let tx_hash = tx.get_hash();
-    let received_time = chrono::offset::Utc::now().timestamp();
-    (
-        txs_mp_db.txs_by_recv_time_write(),
-        txs_mp_db.txs_by_issuer_write(),
-        txs_mp_db.txs_by_recipient_write(),
-        txs_mp_db.txs_write(),
-    )
-        .write(
-            |(mut txs_by_recv_time, mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
-                control(&tx, &txs)?;
-                // Insert on col `txs_by_recv_time`
-                let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default();
-                hashs.0.insert(tx_hash);
-                txs_by_recv_time.upsert(received_time, hashs);
-                // Insert on col `txs_by_issuer`
-                for pubkey in tx.issuers() {
-                    let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-                    hashs.0.insert(tx.get_hash());
-                    txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs);
-                }
-                // Insert on col `txs_by_recipient`
-                for pubkey in tx.recipients_keys() {
-                    let mut hashs = txs_by_recipient
-                        .get(&PubKeyKeyV2(pubkey))?
-                        .unwrap_or_default();
-                    hashs.0.insert(tx.get_hash());
-                    txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
-                }
-                // Insert tx itself
-                txs.upsert(HashKeyV2(tx_hash), PendingTxDbV2(tx.into_owned()));
-                Ok(())
-            },
-        )
-}
-
-pub fn remove_all_pending_txs<B: Backend>(txs_mp_db: &TxsMpV2Db<B>) -> KvResult<()> {
-    txs_mp_db.txs_by_recv_time_write().clear()?;
-    txs_mp_db.txs_by_issuer_write().clear()?;
-    txs_mp_db.txs_by_recipient_write().clear()?;
-    txs_mp_db.txs_write().clear()?;
-
-    Ok(())
-}
-
-pub fn remove_pending_tx_by_hash<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, hash: Hash) -> KvResult<()> {
-    remove_one_pending_tx(&txs_mp_db, hash)?;
-    Ok(())
-}
-
-pub fn revert_block<B: Backend>(
-    gva_db: &GvaV1Db<B>,
-    txs_mp_db: &TxsMpV2Db<B>,
-    block: DubpBlockV10Stringified,
-    gva: bool,
-) -> KvResult<()> {
-    for tx in &block.transactions {
-        let tx_hash = if let Some(ref tx_hash) = tx.hash {
-            Hash::from_hex(&tx_hash)
-                .map_err(|e| KvError::DeserError(format!("Transaction with invalid hash: {}", e)))?
-        } else {
-            return Err(KvError::DeserError(
-                "Try to revert a block that contains a transaction without hash !".to_owned(),
-            ));
-        };
-        if gva {
-            let tx = tx::revert_tx(gva_db, &tx_hash)?.ok_or_else(|| {
-                KvError::DbCorrupted(format!("GVA: tx '{}' dont exist on txs history.", tx_hash,))
-            })?;
-            add_pending_tx(|_, _| Ok(()), txs_mp_db, Cow::Owned(tx))?;
-        } else {
-            add_pending_tx(
-                |_, _| Ok(()),
-                txs_mp_db,
-                Cow::Owned(
-                    TransactionDocumentV10::from_string_object(&tx).map_err(|e| {
-                        KvError::DeserError(format!("Block with invalid tx: {}", e))
-                    })?,
-                ),
-            )?;
-        }
-    }
-
-    identities::revert_identities(gva_db, &block)?;
-
-    Ok(())
-}
-
-pub fn apply_block<B: Backend>(
-    gva_db: &GvaV1Db<B>,
-    txs_mp_db: &TxsMpV2Db<B>,
-    block: DubpBlockV10Stringified,
-    gva: bool,
-) -> KvResult<()> {
-    let block_hash = if let Some(ref block_hash_str) = block.hash {
-        Hash::from_hex(&block_hash_str)
-            .map_err(|_| KvError::DeserError(format!("Hash '{}' is invalid", block_hash_str)))?
-    } else {
-        return Err(KvError::DeserError(format!(
-            "Block #{} is without hash",
-            block.number
-        )));
-    };
-    let blockstamp = Blockstamp {
-        number: BlockNumber(block.number as u32),
-        hash: BlockHash(block_hash),
-    };
-    let txs = block
-        .transactions
-        .iter()
-        .map(|tx_str| TransactionDocumentV10::from_string_object(tx_str))
-        .collect::<Result<Vec<TransactionDocumentV10>, TextParseError>>()
-        .map_err(|e| KvError::DeserError(format!("Invalid transaction in block: {}", e)))?;
-    write_block_txs(
-        &txs_mp_db,
-        &gva_db,
-        blockstamp,
-        block.median_time as i64,
-        gva,
-        txs,
-    )?;
-
-    if gva {
-        identities::update_identities(&gva_db, &block)?;
-    }
-
-    Ok(())
-}
-
-#[inline(always)]
-pub fn apply_chunk_of_blocks<B: Backend>(
-    gva_db: &GvaV1Db<B>,
-    txs_mp_db: &TxsMpV2Db<B>,
-    blocks: Vec<DubpBlockV10Stringified>,
-    gva: bool,
-) -> KvResult<()> {
-    for block in blocks {
-        if block.number > 300_000 {
-            log::info!("apply_block(#{})", block.number);
-        }
-        apply_block(gva_db, txs_mp_db, block, gva)?;
-    }
-    Ok(())
-}
-
-fn write_block_txs<B: Backend>(
-    txs_mp_db: &TxsMpV2Db<B>,
-    gva_db: &GvaV1Db<B>,
-    current_blockstamp: Blockstamp,
-    current_time: i64,
-    gva: bool,
-    txs: Vec<TransactionDocumentV10>,
-) -> KvResult<()> {
-    for tx in txs {
-        let tx_hash = tx.get_hash();
-        // Remove tx from mempool
-        remove_one_pending_tx(&txs_mp_db, tx_hash)?;
-        // Write tx and update sources
-        if gva {
-            tx::write_gva_tx(current_blockstamp, current_time, &gva_db, tx_hash, tx)?;
-        }
-    }
-    Ok(())
-}
-
-pub fn trim_expired_non_written_txs<B: Backend>(
-    txs_mp_db: &TxsMpV2Db<B>,
-    limit_time: i64,
-) -> KvResult<()> {
-    // Get hashs of tx to remove and "times" to remove
-    let mut times = Vec::new();
-    let hashs = txs_mp_db.txs_by_recv_time().iter(..limit_time, |it| {
-        it.map_ok(|(k, v)| {
-            times.push(k);
-            v.0
-        })
-        .flatten_ok()
-        .collect::<KvResult<SmallVec<[Hash; 4]>>>()
-    })?;
-    // For each tx to remove
-    for (hash, time) in hashs.into_iter().zip(times.into_iter()) {
-        remove_one_pending_tx(&txs_mp_db, hash)?;
-        // Remove txs hashs in col `txs_by_recv_time`
-        txs_mp_db.txs_by_recv_time_write().remove(time)?;
-    }
-
-    Ok(())
-}
-
-fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) -> KvResult<bool> {
-    if let Some(tx) = txs_mp_db.txs().get(&HashKeyV2(tx_hash))? {
-        // Remove tx hash in col `txs_by_issuer`
-        for pubkey in tx.0.issuers() {
-            let mut hashs_ = txs_mp_db
-                .txs_by_issuer()
-                .get(&PubKeyKeyV2(pubkey))?
-                .unwrap_or_default();
-            hashs_.0.remove(&tx_hash);
-            txs_mp_db
-                .txs_by_issuer_write()
-                .upsert(PubKeyKeyV2(pubkey), hashs_)?
-        }
-        // Remove tx hash in col `txs_by_recipient`
-        for pubkey in tx.0.recipients_keys() {
-            let mut hashs_ = txs_mp_db
-                .txs_by_recipient()
-                .get(&PubKeyKeyV2(pubkey))?
-                .unwrap_or_default();
-            hashs_.0.remove(&tx_hash);
-            txs_mp_db
-                .txs_by_recipient_write()
-                .upsert(PubKeyKeyV2(pubkey), hashs_)?
-        }
-        // Remove tx itself
-        txs_mp_db.txs_write().remove(HashKeyV2(tx_hash))?;
-        Ok(true)
-    } else {
-        Ok(false)
-    }
+pub struct UtxoV10 {
+    pub id: UtxoIdV10,
+    pub amount: SourceAmount,
+    pub script: WalletScriptV10,
+    pub written_time: i64,
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use dubp::documents::transaction::TransactionDocumentV10Stringified;
+    use dubp::{
+        documents::transaction::TransactionDocumentV10Stringified,
+        documents_parser::prelude::FromStringObject,
+    };
 
     #[test]
     #[ignore]
@@ -291,11 +71,11 @@ mod tests {
                 .path("/home/elois/.config/duniter/s2/data/gva_v1_sled")
                 .flush_every_ms(None),
         )?;
-        let txs_mp_db = TxsMpV2Db::<Sled>::open(
+        /*let txs_mp_db = TxsMpV2Db::<Sled>::open(
             SledConf::default()
                 .path("/home/elois/.config/duniter/s2/data/txs_mp_v2_sled")
                 .flush_every_ms(None),
-        )?;
+        )?;*/
 
         let txs: Vec<TransactionDocumentV10Stringified> = serde_json::from_str(r#"[
             {
@@ -403,8 +183,9 @@ mod tests {
             transactions: txs,
             ..Default::default()
         };
+        let block = DubpBlockV10::from_string_object(&block).expect("fail to parse block");
 
-        apply_block(&gva_db, &txs_mp_db, block, true)?;
+        gva::apply_block(&block, &gva_db)?;
 
         Ok(())
     }
diff --git a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3765a2d4614d41fe6303a68954b797782b0a280f
--- /dev/null
+++ b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
@@ -0,0 +1,154 @@
+//  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::*;
+
+pub fn apply_block<B: Backend>(
+    block_txs: &[TransactionDocumentV10],
+    txs_mp_db: &TxsMpV2Db<B>,
+) -> KvResult<()> {
+    for tx in block_txs {
+        // Remove tx from mempool
+        remove_one_pending_tx(&txs_mp_db, tx.get_hash())?;
+    }
+    Ok(())
+}
+
+pub fn revert_block<B: Backend>(
+    block_txs: &[TransactionDocumentV10],
+    txs_mp_db: &TxsMpV2Db<B>,
+) -> KvResult<()> {
+    for tx in block_txs {
+        // Rewrite tx on mempool
+        add_pending_tx(|_, _| Ok(()), txs_mp_db, Cow::Borrowed(tx))?;
+    }
+    Ok(())
+}
+
+pub fn add_pending_tx<
+    B: Backend,
+    F: FnOnce(
+        &TransactionDocumentV10,
+        &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxEvent>,
+    ) -> KvResult<()>,
+>(
+    control: F,
+    txs_mp_db: &TxsMpV2Db<B>,
+    tx: Cow<TransactionDocumentV10>,
+) -> KvResult<()> {
+    let tx_hash = tx.get_hash();
+    let received_time = chrono::offset::Utc::now().timestamp();
+    (
+        txs_mp_db.txs_by_recv_time_write(),
+        txs_mp_db.txs_by_issuer_write(),
+        txs_mp_db.txs_by_recipient_write(),
+        txs_mp_db.txs_write(),
+    )
+        .write(
+            |(mut txs_by_recv_time, mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
+                control(&tx, &txs)?;
+                // Insert on col `txs_by_recv_time`
+                let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default();
+                hashs.0.insert(tx_hash);
+                txs_by_recv_time.upsert(received_time, hashs);
+                // Insert on col `txs_by_issuer`
+                for pubkey in tx.issuers() {
+                    let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
+                    hashs.0.insert(tx.get_hash());
+                    txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs);
+                }
+                // Insert on col `txs_by_recipient`
+                for pubkey in tx.recipients_keys() {
+                    let mut hashs = txs_by_recipient
+                        .get(&PubKeyKeyV2(pubkey))?
+                        .unwrap_or_default();
+                    hashs.0.insert(tx.get_hash());
+                    txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
+                }
+                // Insert tx itself
+                txs.upsert(HashKeyV2(tx_hash), PendingTxDbV2(tx.into_owned()));
+                Ok(())
+            },
+        )
+}
+
+pub fn remove_all_pending_txs<B: Backend>(txs_mp_db: &TxsMpV2Db<B>) -> KvResult<()> {
+    txs_mp_db.txs_by_recv_time_write().clear()?;
+    txs_mp_db.txs_by_issuer_write().clear()?;
+    txs_mp_db.txs_by_recipient_write().clear()?;
+    txs_mp_db.txs_write().clear()?;
+
+    Ok(())
+}
+
+pub fn remove_pending_tx_by_hash<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, hash: Hash) -> KvResult<()> {
+    remove_one_pending_tx(&txs_mp_db, hash)?;
+    Ok(())
+}
+
+pub fn trim_expired_non_written_txs<B: Backend>(
+    txs_mp_db: &TxsMpV2Db<B>,
+    limit_time: i64,
+) -> KvResult<()> {
+    // Get hashs of tx to remove and "times" to remove
+    let mut times = Vec::new();
+    let hashs = txs_mp_db.txs_by_recv_time().iter(..limit_time, |it| {
+        it.map_ok(|(k, v)| {
+            times.push(k);
+            v.0
+        })
+        .flatten_ok()
+        .collect::<KvResult<SmallVec<[Hash; 4]>>>()
+    })?;
+    // For each tx to remove
+    for (hash, time) in hashs.into_iter().zip(times.into_iter()) {
+        remove_one_pending_tx(&txs_mp_db, hash)?;
+        // Remove txs hashs in col `txs_by_recv_time`
+        txs_mp_db.txs_by_recv_time_write().remove(time)?;
+    }
+
+    Ok(())
+}
+
+fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) -> KvResult<bool> {
+    if let Some(tx) = txs_mp_db.txs().get(&HashKeyV2(tx_hash))? {
+        (
+            txs_mp_db.txs_by_issuer_write(),
+            txs_mp_db.txs_by_recipient_write(),
+            txs_mp_db.txs_write(),
+        )
+            .write(|(mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
+                // Remove tx hash in col `txs_by_issuer`
+                for pubkey in tx.0.issuers() {
+                    let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
+                    hashs_.0.remove(&tx_hash);
+                    txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
+                }
+                // Remove tx hash in col `txs_by_recipient`
+                for pubkey in tx.0.recipients_keys() {
+                    let mut hashs_ = txs_by_recipient
+                        .get(&PubKeyKeyV2(pubkey))?
+                        .unwrap_or_default();
+                    hashs_.0.remove(&tx_hash);
+                    txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_)
+                }
+                // Remove tx itself
+                txs.remove(HashKeyV2(tx_hash));
+                Ok(true)
+            })
+    } else {
+        Ok(false)
+    }
+}
diff --git a/rust-libs/duniter-dbs/Cargo.toml b/rust-libs/duniter-dbs/Cargo.toml
index 1dfc931750d6b7a8d2acf52442f99247dfacc56b..f7c5bf46e3fb93c2dfe44ceda057fc7180669606 100644
--- a/rust-libs/duniter-dbs/Cargo.toml
+++ b/rust-libs/duniter-dbs/Cargo.toml
@@ -20,10 +20,12 @@ dubp = { version = "0.29.0" }
 kv_typed = { path = "../tools/kv_typed", default-features = false }
 log = "0.4.8"
 mockall = { version = "0.8.0", optional = true }
+rand = "0.7.3"
 serde = { version = "1.0.105", features = ["derive"] }
 serde_json = "1.0.53"
 smallvec = { version = "1.4.0", features = ["serde", "write"] }
 thiserror = "1.0.20"
+uninit = "0.4.0"
 zerocopy = "0.3.0"
 
 [dev-dependencies]
diff --git a/rust-libs/duniter-dbs-write-ops/src/identities.rs b/rust-libs/duniter-dbs/src/bc_v2.rs
similarity index 58%
rename from rust-libs/duniter-dbs-write-ops/src/identities.rs
rename to rust-libs/duniter-dbs/src/bc_v2.rs
index c572294a4e2bc4d50e93272f407cf77ef43b8b05..79db9a3913429ddc6a866a32f24eecde76a26de8 100644
--- a/rust-libs/duniter-dbs-write-ops/src/identities.rs
+++ b/rust-libs/duniter-dbs/src/bc_v2.rs
@@ -15,23 +15,12 @@
 
 use crate::*;
 
-pub(crate) fn update_identities<B: Backend>(
-    _gva_db: &GvaV1Db<B>,
-    block: &DubpBlockV10Stringified,
-) -> KvResult<()> {
-    for joiner in &block.joiners {
-        let joiner_fields: SmallVec<[&str; 5]> = joiner.split(':').collect();
-        let _pubkey = joiner_fields[0];
-        let _username = joiner_fields[4];
-    }
-    // TODO
-    Ok(())
-}
-
-pub(crate) fn revert_identities<B: Backend>(
-    _gva_db: &GvaV1Db<B>,
-    _block: &DubpBlockV10Stringified,
-) -> KvResult<()> {
-    // TODO
-    Ok(())
-}
+db_schema!(
+    BcV2,
+    [
+        ["blocks_meta", blocks_meta, BlockNumberKeyV2, BlockMetaV2,],
+        ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,],
+        ["identities", identities, PubKeyKeyV2, IdtyDbV2,],
+        ["uds", uds, UdIdV2, EmptyValue,],
+    ]
+);
diff --git a/rust-libs/duniter-dbs/src/gva_v1.rs b/rust-libs/duniter-dbs/src/gva_v1.rs
index 88aab2d60cd86a169d0d40c6cb7ccaf6e6def42e..a3623490b836dcb3d61d1b3a4dd7ea9a5ff53637 100644
--- a/rust-libs/duniter-dbs/src/gva_v1.rs
+++ b/rust-libs/duniter-dbs/src/gva_v1.rs
@@ -18,6 +18,7 @@ use crate::*;
 db_schema!(
     GvaV1,
     [
+        ["uids_index", uids_index, String, PubKeyValV2,],
         ["txs", txs, HashKeyV2, TxDbV2,],
         ["txs_by_issuer", txs_by_issuer, PubKeyKeyV2, HashBTSetV2,],
         [
@@ -38,8 +39,5 @@ db_schema!(
             WalletConditionsV2,
             UtxosOfScriptV1,
         ],
-        ["identities", identities, PubKeyKeyV2, IdtyDbV2,],
-        ["uds", uds_by_block, BlockNumberKeyV2, SourceAmountValV2,],
-        ["uds", uds_by_issuer, PubKeyKeyV2, BlockNumberArrayV2,],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/keys.rs b/rust-libs/duniter-dbs/src/keys.rs
index d05a345d404619552de75b41844f14667445875c..35cb4e6dc5aa8456eb1296ae661c9f164e920f2c 100644
--- a/rust-libs/duniter-dbs/src/keys.rs
+++ b/rust-libs/duniter-dbs/src/keys.rs
@@ -21,5 +21,6 @@ pub mod pubkey;
 pub mod pubkey_and_sig;
 pub mod source_key;
 pub mod timestamp;
+pub mod ud_id;
 pub mod uid;
 pub mod wallet_conditions;
diff --git a/rust-libs/duniter-dbs/src/keys/ud_id.rs b/rust-libs/duniter-dbs/src/keys/ud_id.rs
new file mode 100644
index 0000000000000000000000000000000000000000..71617b5fdf0f3df857110a2b7e790913ee5c5998
--- /dev/null
+++ b/rust-libs/duniter-dbs/src/keys/ud_id.rs
@@ -0,0 +1,123 @@
+//  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::*;
+use uninit::prelude::*;
+
+#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
+pub struct UdIdV2(pub PublicKey, pub BlockNumber);
+
+impl PartialOrd for UdIdV2 {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        match self.0.partial_cmp(&other.0) {
+            Some(std::cmp::Ordering::Equal) => self.1.partial_cmp(&other.1),
+            o => o,
+        }
+    }
+}
+impl Ord for UdIdV2 {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.0.cmp(&other.0) {
+            std::cmp::Ordering::Equal => self.1.cmp(&other.1),
+            o => o,
+        }
+    }
+}
+
+impl KeyAsBytes for UdIdV2 {
+    fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
+        let mut buffer = uninit_array![u8; 37];
+        let (pubkey_buffer, block_number_buffer) = buffer.as_out().split_at_out(33);
+        let pubkey_buffer = pubkey_buffer.copy_from_slice(self.0.as_ref());
+        block_number_buffer.copy_from_slice(&(self.1).0.to_be_bytes());
+        f(unsafe { std::slice::from_raw_parts_mut(pubkey_buffer.as_mut_ptr(), 37) })
+    }
+}
+
+impl FromBytes for UdIdV2 {
+    type Err = StringErr;
+
+    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
+        let pubkey = PublicKey::try_from(&bytes[..33])
+            .map_err(|e| StringErr(format!("{}: {:?}", e, bytes)))?;
+        let block_number = BlockNumber(
+            zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(&bytes[33..])
+                .ok_or_else(|| {
+                    StringErr(
+                        "Corrupted DB: BlockNumber bytes are invalid length or unaligned"
+                            .to_owned(),
+                    )
+                })?
+                .get(),
+        );
+        Ok(UdIdV2(pubkey, block_number))
+    }
+}
+
+impl ToDumpString for UdIdV2 {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableKey for UdIdV2 {
+    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+        let mut source = source.split(':');
+        if let Some(pubkey_str) = source.next() {
+            let pubkey = PublicKey::from_base58(&pubkey_str)
+                .map_err(|e| StringErr(format!("{}: {}", e, pubkey_str)))?;
+            if let Some(block_number_str) = source.next() {
+                Ok(UdIdV2(
+                    pubkey,
+                    BlockNumber::from_str(block_number_str)
+                        .map_err(|e| StringErr(format!("{}", e)))?,
+                ))
+            } else {
+                Err(StringErr("UdIdV2: Invalid format".to_owned()))
+            }
+        } else {
+            Err(StringErr("UdIdV2: Invalid format".to_owned()))
+        }
+    }
+    fn to_explorer_string(&self) -> KvResult<String> {
+        Ok(format!("{}:{}", self.0.to_base58(), (self.1).0))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn ud_id_v2_as_bytes() -> std::result::Result<(), StringErr> {
+        let ud_id = UdIdV2(PublicKey::default(), BlockNumber(3));
+
+        let ud_id_2_res = ud_id.as_bytes(|bytes| {
+            assert_eq!(
+                bytes,
+                [
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 3
+                ]
+            );
+            UdIdV2::from_bytes(bytes)
+        });
+
+        assert_eq!(ud_id_2_res?, ud_id);
+
+        Ok(())
+    }
+}
diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs
index 01dd5e49e3da1c0c5dccfd87e88e05ac3783e662..e4fc04be2df8289d8a049ddb42fdaa87bf383dc4 100644
--- a/rust-libs/duniter-dbs/src/lib.rs
+++ b/rust-libs/duniter-dbs/src/lib.rs
@@ -23,9 +23,11 @@
 )]
 
 mod bc_v1;
+pub mod bc_v2;
 mod errors;
 pub mod gva_v1;
 mod keys;
+mod open_dbs;
 pub mod txs_mp_v2;
 mod values;
 
@@ -50,8 +52,9 @@ pub mod prelude {
     };
 }
 
-// Export technical types
+// Export technical types and functions
 pub use crate::errors::Result;
+pub use crate::open_dbs::open_dbs;
 
 // Export profession types
 pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlockEvent, UidEvent};
@@ -64,11 +67,13 @@ pub use keys::pubkey::{PubKeyKeyV1, PubKeyKeyV2};
 pub use keys::pubkey_and_sig::PubKeyAndSigV1;
 pub use keys::source_key::SourceKeyV1;
 pub use keys::timestamp::TimestampKeyV1;
+pub use keys::ud_id::UdIdV2;
 pub use keys::uid::UidKeyV1;
 pub use keys::wallet_conditions::{WalletConditionsV1, WalletConditionsV2};
 pub use txs_mp_v2::{TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbRo, TxsMpV2DbWritable};
 pub use values::block_db::{BlockDbEnum, BlockDbV1, TransactionInBlockDbV1};
 pub use values::block_head_db::BlockHeadDbV1;
+pub use values::block_meta::BlockMetaV2;
 pub use values::block_number_array_db::{BlockNumberArrayV1, BlockNumberArrayV2};
 pub use values::cindex_db::CIndexDbV1;
 pub use values::hash_array_db::HashBTSetV2;
@@ -76,7 +81,7 @@ pub use values::idty_db::IdtyDbV2;
 pub use values::iindex_db::IIndexDbV1;
 pub use values::kick_db::KickDbV1;
 pub use values::mindex_db::MIndexDbV1;
-pub use values::pubkey_db::{PublicKeyArrayDbV1, PublicKeySingletonDbV1};
+pub use values::pubkey_db::{PubKeyValV2, PublicKeyArrayDbV1, PublicKeySingletonDbV1};
 pub use values::sindex_db::{SIndexDBV1, SourceKeyArrayDbV1};
 pub use values::source_amount::SourceAmountValV2;
 pub use values::tx_db::{PendingTxDbV2, TxDbV2};
@@ -104,6 +109,7 @@ pub(crate) use std::{
     convert::TryFrom,
     fmt::Debug,
     iter::Iterator,
+    path::{Path, PathBuf},
     str::FromStr,
 };
 
@@ -114,12 +120,13 @@ pub trait ToDumpString {
 #[cfg(feature = "mem")]
 pub type DbsBackend = kv_typed::backend::memory::Mem;
 #[cfg(all(not(feature = "mem"), target_arch = "x86_64"))]
-pub type DbsBackend = Lmdb;
+pub type DbsBackend = Sled; // TODO ESZ
 #[cfg(all(not(feature = "mem"), not(target_arch = "x86_64")))]
 pub type DbsBackend = Sled;
 
 #[derive(Clone, Debug)]
 pub struct DuniterDbs {
+    pub bc_db: bc_v2::BcV2Db<DbsBackend>,
     pub gva_db: GvaV1Db<DbsBackend>,
     pub txs_mp_db: TxsMpV2Db<DbsBackend>,
 }
diff --git a/rust-libs/duniter-server/src/conf.rs b/rust-libs/duniter-dbs/src/open_dbs.rs
similarity index 62%
rename from rust-libs/duniter-server/src/conf.rs
rename to rust-libs/duniter-dbs/src/open_dbs.rs
index 63ab05afd4131da648a2bd16d9bac1832930dd92..5dab56843a829b69597d274e5cccb0f01fea2f16 100644
--- a/rust-libs/duniter-server/src/conf.rs
+++ b/rust-libs/duniter-dbs/src/open_dbs.rs
@@ -13,19 +13,18 @@
 // 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::bc_v2::BcV2DbWritable as _;
 use crate::*;
 
-#[derive(Clone, Debug)]
-pub struct DuniterServerConf {
-    pub gva: Option<GvaConf>,
-    pub server_pubkey: PublicKey,
-    pub txs_mempool_size: usize,
-}
-
 pub fn open_dbs(home_path_opt: Option<&Path>) -> DuniterDbs {
     DuniterDbs {
+        bc_db: crate::bc_v2::BcV2Db::<DbsBackend>::open(DbsBackend::gen_backend_conf(
+            "bc_v2",
+            home_path_opt,
+        ))
+        .expect("fail to open BcV2 DB"),
         gva_db: GvaV1Db::<DbsBackend>::open(DbsBackend::gen_backend_conf("gva_v1", home_path_opt))
-            .expect("fail to open GVA DB"),
+            .expect("fail to open Gva DB"),
         txs_mp_db: TxsMpV2Db::<DbsBackend>::open(DbsBackend::gen_backend_conf(
             "txs_mp_v2",
             home_path_opt,
@@ -68,7 +67,30 @@ impl BackendConf for Lmdb {
 impl BackendConf for Sled {
     #[inline(always)]
     fn gen_backend_conf(db_name: &'static str, home_path_opt: Option<&Path>) -> SledConf {
-        let conf = SledConf::default().flush_every_ms(Some(10_000));
+        let mut conf = SledConf::default().flush_every_ms(Some(10_000));
+        conf = match db_name {
+            "bc_v2" => {
+                if let Ok(compression_level) = std::env::var("DUNITER_BC_DB_COMPRESSION") {
+                    conf.use_compression(true)
+                        .compression_factor(i32::from_str(&compression_level).expect(
+                        "Env var DUNITER_BC_DB_COMPRESSION must be a number beetween 1 and 22 !",
+                    ))
+                } else {
+                    conf.use_compression(false)
+                }
+            }
+            "gva_v1" => {
+                if let Ok(compression_level) = std::env::var("DUNITER_GVA_DB_COMPRESSION") {
+                    conf.use_compression(true)
+                        .compression_factor(i32::from_str(&compression_level).expect(
+                        "Env var DUNITER_GVA_DB_COMPRESSION must be a number beetween 1 and 22 !",
+                    ))
+                } else {
+                    conf.use_compression(false)
+                }
+            }
+            _ => conf.use_compression(false),
+        };
         if let Some(data_path) = home_path_opt {
             conf.path(data_path.join(format!("data/{}_sled", db_name)))
         } else {
diff --git a/rust-libs/duniter-dbs/src/values.rs b/rust-libs/duniter-dbs/src/values.rs
index 62427e53eb8cae5ffc8b497340a110251a61cd78..7c6823de95323b4f9856a8857e3d7a687bbb7101 100644
--- a/rust-libs/duniter-dbs/src/values.rs
+++ b/rust-libs/duniter-dbs/src/values.rs
@@ -15,6 +15,7 @@
 
 pub mod block_db;
 pub mod block_head_db;
+pub mod block_meta;
 pub mod block_number_array_db;
 pub mod cindex_db;
 pub mod hash_array_db;
diff --git a/rust-libs/duniter-dbs/src/values/block_meta.rs b/rust-libs/duniter-dbs/src/values/block_meta.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8cd6ca2e39abfaf79131e83fdd0ed29683aa43d6
--- /dev/null
+++ b/rust-libs/duniter-dbs/src/values/block_meta.rs
@@ -0,0 +1,97 @@
+//  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::*;
+
+const BLOCK_META_SERIALIZED_SIZE: usize = 319;
+
+#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
+pub struct BlockMetaV2 {
+    pub version: u64,                   // 8
+    pub number: u32,                    // 4
+    pub hash: Hash,                     // 32
+    pub signature: Signature,           // 64
+    pub inner_hash: Hash,               // 32
+    pub previous_hash: Hash,            // 32
+    pub issuer: PublicKey,              // 33
+    pub previous_issuer: PublicKey,     // 33
+    pub time: u64,                      // 8
+    pub pow_min: u32,                   // 4
+    pub members_count: u64,             // 8
+    pub issuers_count: u32,             // 4
+    pub issuers_frame: u64,             // 8
+    pub issuers_frame_var: i64,         // 8
+    pub median_time: u64,               // 8
+    pub nonce: u64,                     // 8
+    pub monetary_mass: u64,             // 8
+    pub dividend: Option<SourceAmount>, // 17 -> TOTAL SIZE == 331 bytes
+}
+
+impl ValueAsBytes for BlockMetaV2 {
+    fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
+        let mut buffer = [0u8; BLOCK_META_SERIALIZED_SIZE];
+        bincode::serialize_into(&mut buffer[..], self).unwrap_or_else(|_| unreachable!());
+        f(buffer.as_ref())
+    }
+}
+
+impl kv_typed::prelude::FromBytes for BlockMetaV2 {
+    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 BlockMetaV2 {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableValue for BlockMetaV2 {
+    fn from_explorer_str(json_str: &str) -> std::result::Result<Self, StringErr> {
+        Ok(serde_json::from_str(&json_str)
+            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+    }
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use unwrap::unwrap;
+
+    #[test]
+    fn block_meta_v2_as_bytes() -> KvResult<()> {
+        assert_eq!(
+            unwrap!(bincode::serialized_size(&BlockMetaV2 {
+                dividend: Some(SourceAmount::new(42, 0)),
+                ..Default::default()
+            })),
+            BLOCK_META_SERIALIZED_SIZE as u64
+        );
+        let bloc_meta = BlockMetaV2::default();
+
+        let bm2_res = bloc_meta.as_bytes(|bytes| Ok(unwrap!(BlockMetaV2::from_bytes(bytes))));
+
+        assert_eq!(bm2_res?, bloc_meta);
+
+        Ok(())
+    }
+}
diff --git a/rust-libs/duniter-dbs/src/values/idty_db.rs b/rust-libs/duniter-dbs/src/values/idty_db.rs
index 3e60717844c3333a9ef8e55633f08d54b0f2ddd2..f12a27fbe2a395c94472a233b04655746f596c06 100644
--- a/rust-libs/duniter-dbs/src/values/idty_db.rs
+++ b/rust-libs/duniter-dbs/src/values/idty_db.rs
@@ -17,8 +17,8 @@ use crate::*;
 
 #[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
 pub struct IdtyDbV2 {
-    member: bool,
-    username: String,
+    pub member: bool,
+    pub username: String,
 }
 
 impl ValueAsBytes for IdtyDbV2 {
diff --git a/rust-libs/duniter-dbs/src/values/pubkey_db.rs b/rust-libs/duniter-dbs/src/values/pubkey_db.rs
index 78e750ef65a72939f504e007b9920e4e948d301c..17be3a7528442c809f7937d4c3266d98368f6e7c 100644
--- a/rust-libs/duniter-dbs/src/values/pubkey_db.rs
+++ b/rust-libs/duniter-dbs/src/values/pubkey_db.rs
@@ -15,6 +15,8 @@
 
 use crate::*;
 
+// V1
+
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub struct PublicKeySingletonDbV1(pub PublicKey);
 
@@ -107,3 +109,42 @@ impl ExplorableValue for PublicKeyArrayDbV1 {
         ))
     }
 }
+
+// V2
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct PubKeyValV2(pub PublicKey);
+
+impl ValueAsBytes for PubKeyValV2 {
+    fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
+        f(self.0.as_ref())
+    }
+}
+
+impl kv_typed::prelude::FromBytes for PubKeyValV2 {
+    type Err = StringErr;
+
+    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
+        Ok(PubKeyValV2(
+            PublicKey::try_from(bytes).map_err(|e| StringErr(format!("{}: {:?}", e, bytes)))?,
+        ))
+    }
+}
+
+impl ToDumpString for PubKeyValV2 {
+    fn to_dump_string(&self) -> String {
+        todo!()
+    }
+}
+
+#[cfg(feature = "explorer")]
+impl ExplorableValue for PubKeyValV2 {
+    fn from_explorer_str(pubkey_str: &str) -> std::result::Result<Self, StringErr> {
+        Ok(PubKeyValV2(PublicKey::from_base58(&pubkey_str).map_err(
+            |e| StringErr(format!("{}: {}", e, pubkey_str)),
+        )?))
+    }
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::Value::String(self.0.to_base58()))
+    }
+}
diff --git a/rust-libs/duniter-gva/Cargo.toml b/rust-libs/duniter-gva/Cargo.toml
index 973c4db8d14f1f0bdb3c3cdda8946e51bf4e470b..3e53ba15076044c5d230557502cb2d7780876144 100644
--- a/rust-libs/duniter-gva/Cargo.toml
+++ b/rust-libs/duniter-gva/Cargo.toml
@@ -12,7 +12,7 @@ dubp = { version = "0.29.0" }
 duniter-dbs = { path = "../duniter-dbs" }
 duniter-dbs-read-ops = { path = "../duniter-dbs-read-ops" }
 duniter-mempools = { path = "../duniter-mempools" }
-fast-threadpool = "0.1.1"
+fast-threadpool = "0.2.1"
 flume = "0.9.1"
 futures = "0.3.6"
 http = "0.2.1"
diff --git a/rust-libs/duniter-gva/src/lib.rs b/rust-libs/duniter-gva/src/lib.rs
index 07d6247c46e252b46c739d1bb577e59e5e94aafb..3f8e6cf31cce261fbb6ab09eac3599ade104f99d 100644
--- a/rust-libs/duniter-gva/src/lib.rs
+++ b/rust-libs/duniter-gva/src/lib.rs
@@ -170,6 +170,7 @@ impl GvaServer {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use duniter_dbs::bc_v2::{BcV2Db, BcV2DbWritable};
     use duniter_dbs::kv_typed::backend::memory::{Mem, MemConf};
     use duniter_dbs::{GvaV1Db, GvaV1DbWritable, TxsMpV2Db, TxsMpV2DbWritable};
     use fast_threadpool::ThreadPoolConfig;
@@ -179,6 +180,7 @@ mod tests {
     #[ignore]
     fn launch_mem_gva() {
         let dbs = DuniterDbs {
+            bc_db: unwrap!(BcV2Db::<Mem>::open(MemConf::default())),
             gva_db: unwrap!(GvaV1Db::<Mem>::open(MemConf::default())),
             txs_mp_db: unwrap!(TxsMpV2Db::<Mem>::open(MemConf::default())),
         };
diff --git a/rust-libs/duniter-mempools/src/lib.rs b/rust-libs/duniter-mempools/src/lib.rs
index f6e4b426cbbc0131a9dcad9c05b93f23907051b3..a4528f80fe5e8afa5838f94457ceec916b631cdd 100644
--- a/rust-libs/duniter-mempools/src/lib.rs
+++ b/rust-libs/duniter-mempools/src/lib.rs
@@ -84,10 +84,14 @@ impl TxsMempool {
         if duniter_dbs_read_ops::txs_history::tx_exist(gva_db_ro, tx.get_hash())? {
             Err(TxMpError::TxAlreadyWritten)
         } else if tx.issuers().contains(&server_pubkey) {
-            duniter_dbs_write_ops::add_pending_tx(|_, _| Ok(()), txs_mp_db, Cow::Borrowed(tx))?;
+            duniter_dbs_write_ops::txs_mp::add_pending_tx(
+                |_, _| Ok(()),
+                txs_mp_db,
+                Cow::Borrowed(tx),
+            )?;
             Ok(())
         } else {
-            duniter_dbs_write_ops::add_pending_tx(
+            duniter_dbs_write_ops::txs_mp::add_pending_tx(
                 |_tx, txs| {
                     if txs.count()? < self.max_size {
                         Err(KvError::Custom(TxMpError::Full.into()))
@@ -108,7 +112,7 @@ impl TxsMempool {
         txs_mp_db: &TxsMpV2Db<B>,
         tx: &TransactionDocumentV10,
     ) -> KvResult<()> {
-        duniter_dbs_write_ops::add_pending_tx(|_, _| Ok(()), txs_mp_db, Cow::Borrowed(tx))?;
+        duniter_dbs_write_ops::txs_mp::add_pending_tx(|_, _| Ok(()), txs_mp_db, Cow::Borrowed(tx))?;
         Ok(())
     }
 
diff --git a/rust-libs/duniter-server/Cargo.toml b/rust-libs/duniter-server/Cargo.toml
index 358937e224382b3c1163f19ab257d83248e929f5..77adee8f61ee2ccf4900bbf34143b971fd0dc731 100644
--- a/rust-libs/duniter-server/Cargo.toml
+++ b/rust-libs/duniter-server/Cargo.toml
@@ -12,11 +12,10 @@ duniter-dbs-read-ops = { path = "../duniter-dbs-read-ops" }
 duniter-dbs-write-ops = { path = "../duniter-dbs-write-ops" }
 duniter-gva = { path = "../duniter-gva" }
 duniter-mempools = { path = "../duniter-mempools" }
-fast-threadpool = "0.1.1"
+fast-threadpool = "0.2.1"
 flume = "0.9.1"
 log = "0.4.11"
 resiter = "0.4.0"
-rand = "0.7.3"
 
 [dev-dependencies]
 unwrap = "1.2.1"
diff --git a/rust-libs/duniter-server/src/lib.rs b/rust-libs/duniter-server/src/lib.rs
index 16b5b844701bb62d47e29d46e66f7756ea8e8242..19240c051c2f3caba79af46f945bac82ffefa1b3 100644
--- a/rust-libs/duniter-server/src/lib.rs
+++ b/rust-libs/duniter-server/src/lib.rs
@@ -22,37 +22,42 @@
     unused_import_braces
 )]
 
-mod conf;
-
 pub use duniter_dbs::smallvec;
 use duniter_mempools::{TxMpError, TxsMempool};
 use fast_threadpool::ThreadPoolConfig;
 
-pub use crate::conf::{BackendConf, DuniterServerConf};
 pub use duniter_gva::GvaConf;
 
-use dubp::block::DubpBlockV10Stringified;
-use dubp::common::crypto::hashs::Hash;
 use dubp::common::crypto::keys::ed25519::PublicKey;
 use dubp::common::prelude::*;
 use dubp::documents::{prelude::*, transaction::TransactionDocumentV10};
-use duniter_dbs::prelude::*;
+use dubp::{
+    block::prelude::*, common::crypto::hashs::Hash, documents_parser::prelude::FromStringObject,
+};
 use duniter_dbs::{
-    kv_typed::backend::memory::{Mem, MemConf},
-    kv_typed::backend::sled::Sled,
-    kv_typed::prelude::*,
-    GvaV1Db, GvaV1DbReadable, GvaV1DbWritable, HashKeyV2, PendingTxDbV2, TxsMpV2Db,
-    TxsMpV2DbReadable, TxsMpV2DbWritable,
+    kv_typed::prelude::*, GvaV1DbReadable, HashKeyV2, PendingTxDbV2, TxsMpV2DbReadable,
 };
+use duniter_dbs::{prelude::*, BlockMetaV2};
 use duniter_dbs_read_ops::txs_history::TxsHistory;
 use resiter::filter::Filter;
-use std::{
-    collections::BTreeMap,
-    path::{Path, PathBuf},
-};
+use std::{collections::BTreeMap, path::Path};
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum DuniterCommand {
+    Sync,
+    Start,
+}
+
+#[derive(Clone, Debug)]
+pub struct DuniterServerConf {
+    pub gva: Option<GvaConf>,
+    pub server_pubkey: PublicKey,
+    pub txs_mempool_size: usize,
+}
 
 pub struct DuniterServer {
     conf: DuniterServerConf,
+    current: Option<BlockMetaV2>,
     dbs_pool: fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
     pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxEvent>>>,
     txs_mempool: TxsMempool,
@@ -60,13 +65,23 @@ pub struct DuniterServer {
 
 impl DuniterServer {
     pub fn start(
+        command_name: Option<String>,
         conf: DuniterServerConf,
         home_path_opt: Option<&Path>,
         software_version: &'static str,
     ) -> Self {
+        let command = match command_name.unwrap_or_default().as_str() {
+            "sync" => DuniterCommand::Sync,
+            _ => DuniterCommand::Start,
+        };
+
         let txs_mempool = TxsMempool::new(conf.txs_mempool_size);
 
-        let dbs = conf::open_dbs(home_path_opt);
+        log::info!("open duniter databases...");
+        let dbs = duniter_dbs::open_dbs(home_path_opt);
+        log::info!("Databases successfully opened.");
+        let current =
+            duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db).expect("Fail to get current");
 
         let (s, pending_txs_subscriber) = flume::unbounded();
         dbs.txs_mp_db
@@ -75,26 +90,29 @@ impl DuniterServer {
             .expect("Fail to subscribe to txs col");
 
         let threadpool = if home_path_opt.is_some() {
+            log::info!("start dbs threadpool...");
             let threadpool =
                 fast_threadpool::ThreadPool::start(ThreadPoolConfig::default(), dbs.clone());
 
-            if let Some(mut gva_conf) = conf.gva.clone() {
-                if let Some(remote_path) = std::env::var_os("DUNITER_GVA_REMOTE_PATH") {
-                    gva_conf.remote_path(
-                        remote_path
-                            .into_string()
-                            .expect("Invalid utf8 for Env var DUNITER_GVA_REMOTE_PATH"),
-                    );
+            if command != DuniterCommand::Sync {
+                if let Some(mut gva_conf) = conf.gva.clone() {
+                    if let Some(remote_path) = std::env::var_os("DUNITER_GVA_REMOTE_PATH") {
+                        gva_conf.remote_path(
+                            remote_path
+                                .into_string()
+                                .expect("Invalid utf8 for Env var DUNITER_GVA_REMOTE_PATH"),
+                        );
+                    }
+                    duniter_gva::GvaServer::start(
+                        gva_conf,
+                        dbs,
+                        threadpool.async_handler(),
+                        conf.server_pubkey,
+                        software_version,
+                        txs_mempool,
+                    )
+                    .expect("Fail to start GVA server");
                 }
-                duniter_gva::GvaServer::start(
-                    gva_conf,
-                    dbs,
-                    threadpool.async_handler(),
-                    conf.server_pubkey,
-                    software_version,
-                    txs_mempool,
-                )
-                .expect("Fail to start GVA server");
             }
             threadpool
         } else {
@@ -103,6 +121,7 @@ impl DuniterServer {
 
         DuniterServer {
             conf,
+            current,
             dbs_pool: threadpool.into_sync_handler(),
             pending_txs_subscriber,
             txs_mempool,
@@ -211,49 +230,70 @@ impl DuniterServer {
 
     pub fn remove_all_pending_txs(&self) -> KvResult<()> {
         self.dbs_pool
-            .execute(move |dbs| duniter_dbs_write_ops::remove_all_pending_txs(&dbs.txs_mp_db))
+            .execute(move |dbs| {
+                duniter_dbs_write_ops::txs_mp::remove_all_pending_txs(&dbs.txs_mp_db)
+            })
             .expect("dbs pool disconnected")
     }
     pub fn remove_pending_tx_by_hash(&self, hash: Hash) -> KvResult<()> {
         self.dbs_pool
             .execute(move |dbs| {
-                duniter_dbs_write_ops::remove_pending_tx_by_hash(&dbs.txs_mp_db, hash)
+                duniter_dbs_write_ops::txs_mp::remove_pending_tx_by_hash(&dbs.txs_mp_db, hash)
             })
             .expect("dbs pool disconnected")
     }
-    pub fn revert_block(&self, block: DubpBlockV10Stringified) -> KvResult<()> {
+    pub fn revert_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> {
         let gva = self.conf.gva.is_some();
-        self.dbs_pool
+        let block = DubpBlockV10::from_string_object(&block)
+            .map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        self.current = self
+            .dbs_pool
             .execute(move |dbs| {
-                duniter_dbs_write_ops::revert_block(&dbs.gva_db, &dbs.txs_mp_db, block, gva)
+                duniter_dbs_write_ops::txs_mp::revert_block(block.transactions(), &dbs.txs_mp_db)?;
+                if gva {
+                    duniter_dbs_write_ops::gva::revert_block(&block, &dbs.gva_db)?;
+                }
+                duniter_dbs_write_ops::bc::revert_block(&dbs.bc_db, block)
             })
-            .expect("dbs pool disconnected")
+            .expect("dbs pool disconnected")?;
+        Ok(())
     }
-    pub fn apply_block(&self, block: DubpBlockV10Stringified) -> KvResult<()> {
+    pub fn apply_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> {
         let gva = self.conf.gva.is_some();
-        self.dbs_pool
-            .execute(move |dbs| {
-                duniter_dbs_write_ops::apply_block(&dbs.gva_db, &dbs.txs_mp_db, block, gva)
-            })
-            .expect("dbs pool disconnected")
+        let block = DubpBlockV10::from_string_object(&block)
+            .map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        self.current = Some(duniter_dbs_write_ops::apply_block::apply_block(
+            block,
+            self.current,
+            &self.dbs_pool,
+            gva,
+            false,
+        )?);
+        Ok(())
     }
-    pub fn apply_chunk_of_blocks(&self, blocks: Vec<DubpBlockV10Stringified>) -> KvResult<()> {
+    pub fn apply_chunk_of_blocks(&mut self, blocks: Vec<DubpBlockV10Stringified>) -> KvResult<()> {
+        log::debug!("apply_chunk(#{})", blocks[0].number);
         let gva = self.conf.gva.is_some();
-        self.dbs_pool
-            .execute(move |dbs| {
-                duniter_dbs_write_ops::apply_chunk_of_blocks(
-                    &dbs.gva_db,
-                    &dbs.txs_mp_db,
-                    blocks,
-                    gva,
-                )
-            })
-            .expect("dbs pool disconnected")
+        let blocks = blocks
+            .into_iter()
+            .map(|block| DubpBlockV10::from_string_object(&block))
+            .collect::<Result<Vec<_>, _>>()
+            .map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        self.current = Some(duniter_dbs_write_ops::apply_block::apply_chunk(
+            self.current,
+            &self.dbs_pool,
+            blocks,
+            gva,
+        )?);
+        Ok(())
     }
     pub fn trim_expired_non_written_txs(&self, limit_time: i64) -> KvResult<()> {
         self.dbs_pool
             .execute(move |dbs| {
-                duniter_dbs_write_ops::trim_expired_non_written_txs(&dbs.txs_mp_db, limit_time)
+                duniter_dbs_write_ops::txs_mp::trim_expired_non_written_txs(
+                    &dbs.txs_mp_db,
+                    limit_time,
+                )
             })
             .expect("dbs pool disconnected")
     }
@@ -268,6 +308,7 @@ mod tests {
     #[test]
     fn test_txs_history() -> KvResult<()> {
         let server = DuniterServer::start(
+            None,
             DuniterServerConf {
                 gva: None,
                 server_pubkey: PublicKey::default(),
diff --git a/rust-libs/tools/kv_typed/Cargo.toml b/rust-libs/tools/kv_typed/Cargo.toml
index 68cb443dde41454b4efa46598198a5e1eaf69da6..66d4966da6587e16bbd2155679ecd74488a6909f 100644
--- a/rust-libs/tools/kv_typed/Cargo.toml
+++ b/rust-libs/tools/kv_typed/Cargo.toml
@@ -22,7 +22,7 @@ paste = "1.0.2"
 rayon = { version = "1.3.1", optional = true }
 regex = { version = "1.3.9", optional = true }
 serde_json = { version = "1.0.53", optional = true }
-sled = { version = "0.34.4", optional = true }
+sled = { version = "0.34.4", optional = true, features = ["compression"] }
 smallvec = { version = "1.4.0", features = ["serde", "write"] }
 thiserror = "1.0.20"
 uninit = "0.4.0"
diff --git a/rust-libs/tools/kv_typed/benches/compare_backends.rs b/rust-libs/tools/kv_typed/benches/compare_backends.rs
index ac8e2d92b1ab46012df03e8b2b2a178d78424136..8eef7214aadb83e453c1539abe00cc3d5f20e369 100644
--- a/rust-libs/tools/kv_typed/benches/compare_backends.rs
+++ b/rust-libs/tools/kv_typed/benches/compare_backends.rs
@@ -1,4 +1,4 @@
-use criterion::{criterion_group, criterion_main, AxisScale, Criterion, PlotConfiguration};
+use criterion::{criterion_group, criterion_main, Criterion, /*, AxisScale, PlotConfiguration*/};
 use kv_typed::prelude::*;
 use std::{fmt::Debug, path::PathBuf};
 
diff --git a/rust-libs/tools/kv_typed/src/as_bytes.rs b/rust-libs/tools/kv_typed/src/as_bytes.rs
index 0ba24d8e07edab661687ce0010693134924f3394..b7734bb7c50d53388cf569a7679f3f42288712c7 100644
--- a/rust-libs/tools/kv_typed/src/as_bytes.rs
+++ b/rust-libs/tools/kv_typed/src/as_bytes.rs
@@ -28,7 +28,7 @@ impl KeyAsBytes for () {
     }
     fn fill_bytes(&self, _: &mut [u8]) {}
 }
-impl ValueAsBytes for () {
+impl ValueAsBytes for EmptyValue {
     fn as_bytes<T, F: FnMut(&[u8]) -> Result<T, KvError>>(&self, mut f: F) -> Result<T, KvError> {
         f(&[])
     }
diff --git a/rust-libs/tools/kv_typed/src/batch.rs b/rust-libs/tools/kv_typed/src/batch.rs
index 90b9738dfe577271cb043a536b180400c12db61b..1583d515ac4c162c7abaf15d5a0fe47fcc4cdd59 100644
--- a/rust-libs/tools/kv_typed/src/batch.rs
+++ b/rust-libs/tools/kv_typed/src/batch.rs
@@ -71,6 +71,7 @@ impl<BC: BackendCol, C: DbCollectionRw> Batch<BC, C> {
         let _ = k.as_bytes(|k_bytes| {
             self.tree.insert(IVec::from(k_bytes), None);
         });
+        self.upsert_ops.remove(&k);
         self.delete_ops.insert(k);
     }
     #[doc(hidden)]
diff --git a/rust-libs/tools/kv_typed/src/event.rs b/rust-libs/tools/kv_typed/src/event.rs
index e259e267c5e545b7f3488192c653c0b1bf8fa319..0a2c79abe82bc8ab9cea316804bd8095eacb2e47 100644
--- a/rust-libs/tools/kv_typed/src/event.rs
+++ b/rust-libs/tools/kv_typed/src/event.rs
@@ -29,18 +29,3 @@ pub trait EventTrait: 'static + Debug + PartialEq + Send + Sync {
     fn upsert(k: Self::K, v: Self::V) -> Self;
     fn remove(k: Self::K) -> Self;
 }
-
-impl EventTrait for () {
-    type K = ();
-    type V = ();
-
-    fn clear() -> Self {
-        unimplemented!()
-    }
-    fn upsert(_: Self::K, _: Self::V) -> Self {
-        unimplemented!()
-    }
-    fn remove(_: Self::K) -> Self {
-        unimplemented!()
-    }
-}
diff --git a/rust-libs/tools/kv_typed/src/explorer.rs b/rust-libs/tools/kv_typed/src/explorer.rs
index 9522f03b3bc6ef15e7836aa2babb056dd52452c5..49bfd9ef34a9dee09077ebac231d4e6a6eebd495 100644
--- a/rust-libs/tools/kv_typed/src/explorer.rs
+++ b/rust-libs/tools/kv_typed/src/explorer.rs
@@ -68,6 +68,16 @@ impl ExplorableValue for () {
     }
 }
 
+impl ExplorableValue for EmptyValue {
+    fn from_explorer_str(_: &str) -> Result<Self, StringErr> {
+        Ok(EmptyValue)
+    }
+
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::Value::String(String::with_capacity(0)))
+    }
+}
+
 impl ExplorableValue for String {
     fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
         Ok(source.to_owned())
diff --git a/rust-libs/tools/kv_typed/src/from_bytes.rs b/rust-libs/tools/kv_typed/src/from_bytes.rs
index a8837d27d53aa461f6516a8f3fbd3e0d2dbaa572..1ec49096a8f7cf1c8f99739cbcbda352203d76b8 100644
--- a/rust-libs/tools/kv_typed/src/from_bytes.rs
+++ b/rust-libs/tools/kv_typed/src/from_bytes.rs
@@ -15,6 +15,14 @@ impl FromBytes for () {
     }
 }
 
+impl FromBytes for EmptyValue {
+    type Err = std::convert::Infallible;
+
+    fn from_bytes(_: &[u8]) -> Result<Self, Self::Err> {
+        Ok(EmptyValue)
+    }
+}
+
 macro_rules! impl_from_bytes_for_numbers {
     ($($T:ty),*) => {$(
         impl FromBytes for $T {
diff --git a/rust-libs/tools/kv_typed/src/lib.rs b/rust-libs/tools/kv_typed/src/lib.rs
index 0a4ac7072fb38511a62d44bd211d2b0293c27dbe..2e80ad614235ab39a13e9254f458bec2058397fe 100644
--- a/rust-libs/tools/kv_typed/src/lib.rs
+++ b/rust-libs/tools/kv_typed/src/lib.rs
@@ -85,7 +85,7 @@ pub mod prelude {
     pub use crate::transactional_read::TransactionalRead;
     pub use crate::transactional_write::{DbTxCollectionRw, TransactionalWrite, TxColRw};
     pub use crate::utils::arc::Arc;
-    pub use crate::value::{Value, ValueSliceZc, ValueZc};
+    pub use crate::value::{EmptyValue, Value, ValueSliceZc, ValueZc};
     pub use kv_typed_code_gen::db_schema;
 }
 
diff --git a/rust-libs/tools/kv_typed/src/value.rs b/rust-libs/tools/kv_typed/src/value.rs
index 2391b7bd73ea2276c8948cb783bdad433acab799..c2d383e67b400c1565a6562a6c3ebc2b04c760f5 100644
--- a/rust-libs/tools/kv_typed/src/value.rs
+++ b/rust-libs/tools/kv_typed/src/value.rs
@@ -81,3 +81,6 @@ impl ValueSliceZc for String {
         0
     }
 }
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct EmptyValue;
diff --git a/rust-libs/tools/kv_typed/tests/db_schema.rs b/rust-libs/tools/kv_typed/tests/db_schema.rs
index d0d49b745d7a354bd047cadc0a3556342b861ca1..6026dead8d43f3594ad64b442aaf37c2834b8cba 100644
--- a/rust-libs/tools/kv_typed/tests/db_schema.rs
+++ b/rust-libs/tools/kv_typed/tests/db_schema.rs
@@ -26,7 +26,7 @@ mod tests {
         TestV1,
         [
             ["c1", col_1, i32, String,],
-            ["c2", col_2, usize, i128,],
+            ["c2", col_2, usize, EmptyValue,],
             ["c3", col_3, u64, VecU128],
             ["c4", col_4, u64, BTSetU128],
         ]
@@ -88,6 +88,8 @@ mod tests {
         assert_eq!(d, Some("toto".to_owned()));
 
         assert_eq!(db.col_2().get(&3)?, None,);
+        db.col_2_write().upsert(3, EmptyValue)?;
+        assert_eq!(db.col_2().get(&3)?, Some(EmptyValue),);
 
         db.col_1_write().upsert(5, "tutu".to_owned())?;
 
diff --git a/server.ts b/server.ts
index 46fcc19d01dde9e8256770bbb4bcd7d30f777daa..6cdd3e3dc5a2847aa3e442b8d4f673bb932c5f18 100644
--- a/server.ts
+++ b/server.ts
@@ -351,8 +351,8 @@ export class Server extends stream.Duplex implements HookableServer {
     this.streamPush(res.clone())
   }
 
-  async initDAL(conf:ConfDTO|null = null) {
-    await this.dal.init(this.conf)
+  async initDAL(conf:ConfDTO|null = null, commandName: string|null = null) {
+    await this.dal.init(this.conf, commandName)
     // Maintenance
     let head_1 = await this.dal.bindexDAL.head(1);
     if (head_1) {
diff --git a/test/dal/basic-dal-tests.ts b/test/dal/basic-dal-tests.ts
index 8fc3836d2d9c1668d5a4b47a3c6b46ddfdbc0b97..7891325bc433d04a5fcc0434a52d02a5ca1454f2 100644
--- a/test/dal/basic-dal-tests.ts
+++ b/test/dal/basic-dal-tests.ts
@@ -44,7 +44,7 @@ var mocks = {
     "currency" : "meta_brouzouf",
     "issuer" : "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk",
     "parameters" : "0.1:86400:100:604800:2629800:3:3:2629800:3:11:600:20:144:0.67",
-    "previousHash" : "",
+    "previousHash" : "00063EB6E83F8717CEF1D25B3E2EE30800063EB6E83F8717CEF1D25B3E2EE308",
     "previousIssuer" : "",
     "transactions" : [
     ],
@@ -71,28 +71,28 @@ var mocks = {
       "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw"
     ],
     "leavers" : [
-      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:3cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787800:inso",
-      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:3lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421786393:cgeek",
-      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:3tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421790376:moul",
-      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:3oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787461:galuel"
+      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:3cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso",
+      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:3lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek",
+      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:3tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul",
+      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:3oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel"
     ],
     "actives" : [
-      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:2cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787800:inso",
-      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:2lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421786393:cgeek",
-      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:2tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421790376:moul",
-      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:2oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787461:galuel"
+      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:2cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso",
+      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:2lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek",
+      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:2tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul",
+      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:2oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel"
     ],
     "joiners" : [
-      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:1cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787800:inso",
-      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421786393:cgeek",
-      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:1tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421790376:moul",
-      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:1oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:1421787461:galuel"
+      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:1cJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso",
+      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek",
+      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:1tyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul",
+      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:1oiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel"
     ],
     "identities" : [
-      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:1421787800:inso",
-      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:1421786393:cgeek",
-      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:1421790376:moul",
-      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:1421787461:galuel"
+      "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso",
+      "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek",
+      "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul",
+      "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel"
     ],
     "membersCount" : 4,
     "monetaryMass" : 0,
@@ -104,7 +104,7 @@ var mocks = {
     "powMin" : 3,
     "number" : 0,
     "nonce" : 10144,
-    "inner_hash": "r51009E813AEAB91F6541170D589E42BD2BBBC19BAB18F32EC1D3E83159BB1CD6"
+    "inner_hash": "51009E813AEAB91F6541170D589E42BD2BBBC19BAB18F32EC1D3E83159BB1CD6"
   }
 };
 
diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts
index 5f6394f17264b790ed510fe2c4001b0545923320..571c3c6ed6852f02a0bc6437440119eae2dc59ea 100644
--- a/test/integration/tools/toolbox.ts
+++ b/test/integration/tools/toolbox.ts
@@ -520,6 +520,10 @@ export class TestingServer {
     return blocksResolved
   }
 
+  async revertCurrentBlock() {
+    await this.server.BlockchainService.revertCurrentBlock()
+  }
+
   async resolveExistingBlock(filterFunc: (b: DBBlock) => boolean) {
     const blocksResolved = await this.server.BlockchainService.blockResolution(filterFunc)
     if (!blocksResolved) {
diff --git a/test/integration/ws2p/ws2p_sync.ts b/test/integration/ws2p/ws2p_sync.ts
deleted file mode 100644
index faa75739b062a4be76dcbbfc748902d38da5e8e2..0000000000000000000000000000000000000000
--- a/test/integration/ws2p/ws2p_sync.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
-// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
-//
-// 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.
-
-import {WS2PConstants} from "../../../app/modules/ws2p/lib/constants"
-import {assertEqual, assertNotNull, createCurrencyWith2Blocks, writeBasicTestWith2Users} from "../tools/test-framework"
-import {NewTestingServer, TestWS2PAPI} from "../tools/toolbox"
-import {CrawlerDependency} from "../../../app/modules/crawler/index"
-
-describe('WS2P sync', () => writeBasicTestWith2Users((test) => {
-
-
-  // We want the test to fail quickly
-  WS2PConstants.CONNEXION_TIMEOUT = 1000
-  WS2PConstants.REQUEST_TIMEOUT = 1000
-
-  let ws2p: TestWS2PAPI
-
-  test('should be able to init with 2 blocks', async (s1, cat, tac) => {
-    await createCurrencyWith2Blocks(s1, cat, tac)
-    await s1.disableBMA()
-  })
-
-  test('we should be able to connect for SYNC', async (s1, cat, tac) => {
-    ws2p = await s1.enableWS2P()
-    const ws = ws2p.connectForSync(tac.keypair, '12345678')
-    const current = await ws.getCurrent()
-    assertNotNull(current)
-    assertEqual(2, current.number)
-  })
-
-  test('we should be able to reconnect for SYNC', async (s1, cat, tac) => {
-    const ws = ws2p.connectForSync(tac.keypair, '22222222')
-    await assertNotNull(ws.getCurrent())
-  })
-
-  test('we should be able to connect for SYNC with toc', async (s1, cat, tac, toc) => {
-    const ws = ws2p.connectForSync(toc.keypair, '33333333')
-    const current = await ws.getCurrent()
-    assertNotNull(current)
-    assertEqual(2, current.number)
-  })
-
-  test('we should be able to make a full sync with cat', async (s1, cat, tac, toc) => {
-    const s2 = NewTestingServer({ pair: cat.keypair })
-    await s2.initWithDAL()
-    // We sync on s1
-    await CrawlerDependency.duniter.methods.synchronize(s2._server, ws2p.host, ws2p.port, 2, 2, true).syncPromise
-    assertNotNull(await s2.dal.getCurrentBlockOrNull())
-  })
-}))