diff --git a/3rdparty/crc32c_adler/CMakeLists.txt b/3rdparty/crc32c_adler/CMakeLists.txt
new file mode 100755
index 0000000000000000000000000000000000000000..b660945e8049ca7c527b3dc86ce57ec25fad0ba4
--- /dev/null
+++ b/3rdparty/crc32c_adler/CMakeLists.txt
@@ -0,0 +1,16 @@
+#
+#  DESCRIPTION: A miminal cmake script to be used to produce CRC32 Addler static library
+#
+#  AUTHOR: Ruslan R. Laishev
+#
+#  CREATION DATE: 14-NOV-2022
+#
+#  MODIFICATION HISTORY:
+#
+cmake_minimum_required(VERSION 3.10)
+project(crc32c_adler)
+
+set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall" )
+set( SRCS crc32c_adler.c  crc32c_adler.h)
+add_library(crc32c_adler  STATIC ${SRCS})
+target_link_libraries(crc32c_adler pthread)
diff --git a/3rdparty/crc32c_adler/README b/3rdparty/crc32c_adler/README
new file mode 100755
index 0000000000000000000000000000000000000000..23b609d0e8e43e890a06a4068bc20da327abd40f
--- /dev/null
+++ b/3rdparty/crc32c_adler/README
@@ -0,0 +1,14 @@
+
+Extended version of CRC32C implementation by Mark Adler, providing both hardware (SSE 4.2) and software algorithms with auto-detection.
+
+Source:
+https://stackoverflow.com/a/17646775
+
+According to Ferry Toth, Adler's implementation is 'highly optimized':
+https://stackoverflow.com/a/39862799
+https://github.com/htot/crc32c
+
+Revised software algorithm by Robert Važan:
+https://stackoverflow.com/a/21915223
+https://crc32c.machinezoo.com/
+https://bitbucket.org/robertvazan/crc32c-hw/src
diff --git a/3rdparty/crc32c_adler/crc32c_adler.c b/3rdparty/crc32c_adler/crc32c_adler.c
new file mode 100755
index 0000000000000000000000000000000000000000..e21f8c8ec52e84d170265a285d60caa0c8d9bcb4
--- /dev/null
+++ b/3rdparty/crc32c_adler/crc32c_adler.c
@@ -0,0 +1,501 @@
+/*
+  CRC32C_ADLER -- Computes CRC32C Checksums
+  Version 1.2, Date 05/21/18
+  Copyright (C) 2013 Mark Adler <madler@alumni.caltech.edu>
+  Copyright (C) 2018 Fonic <https://github.com/fonic>
+
+  Provides both a hardware-accelerated algorithm (*) and a software algorithm.
+  Note that this computes CRC32C checksums, not CRC32 (without 'C') checksums
+  used by Ethernet, gzip, etc.
+
+  (*) CRC instruction on Intel SSE 4.2 processors.  SSE 4.2 was first supported
+      by Nehalem processors introduced in November, 2008.
+
+  Version history:
+  1.0  10 Feb 2013  First version
+  1.1   1 Aug 2013  Correct comments on why three crc instructions in parallel
+  1.2  21 May 2018  Add header file, revise hardware support check, eliminate
+                    pthreads, restructure code, revise comments and description
+
+  Version 1.1 by Mark Adler was originally published here:
+  https://stackoverflow.com/a/17646775
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software.  If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "crc32c_adler.h"
+
+/* CRC32C (iSCSI) polynomial in reversed bit order. */
+#define POLY 0x82f63b78
+
+
+/******************************************************************************
+ *                                                                            *
+ *  Software Algorithm (1) - Table-driven, 8 Bytes / Iteration                *
+ *                                                                            *
+ ******************************************************************************/
+
+/* Table for software algorithm. */
+static uint32_t crc32c_table[8][256];
+
+/* Flag to indicate if crc32c_init_sw() has been called. */
+static int crc32c_sw_initialized = 0;
+
+/* Initialize table for software algorithm. */
+static void crc32c_init_sw(void)
+{
+    uint32_t n, crc, k;
+
+    for (n = 0; n < 256; n++) {
+        crc = n;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc32c_table[0][n] = crc;
+    }
+    for (n = 0; n < 256; n++) {
+        crc = crc32c_table[0][n];
+        for (k = 1; k < 8; k++) {
+            crc = crc32c_table[0][crc & 0xff] ^ (crc >> 8);
+            crc32c_table[k][n] = crc;
+        }
+    }
+    crc32c_sw_initialized = 1;
+}
+
+/* Compute CRC32C checksum. */
+uint32_t crc32c_sw(uint32_t crci, const void *buf, size_t len)
+{
+    const unsigned char *next = buf;
+    uint64_t crc;
+
+    if (!crc32c_sw_initialized)
+        crc32c_init_sw();
+
+    crc = crci ^ 0xffffffff;
+    while (len && ((uintptr_t)next & 7) != 0) {
+        crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        len--;
+    }
+    while (len >= 8) {
+        crc ^= *(uint64_t *)next;
+        crc = crc32c_table[7][crc & 0xff] ^
+              crc32c_table[6][(crc >> 8) & 0xff] ^
+              crc32c_table[5][(crc >> 16) & 0xff] ^
+              crc32c_table[4][(crc >> 24) & 0xff] ^
+              crc32c_table[3][(crc >> 32) & 0xff] ^
+              crc32c_table[2][(crc >> 40) & 0xff] ^
+              crc32c_table[1][(crc >> 48) & 0xff] ^
+              crc32c_table[0][crc >> 56];
+        next += 8;
+        len -= 8;
+    }
+    while (len) {
+        crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        len--;
+    }
+    return (uint32_t)crc ^ 0xffffffff;
+}
+
+
+/******************************************************************************
+ *                                                                            *
+ *  Software Algorithm (2) - Table-driven, 16 Bytes / Iteration               *
+ *                                                                            *
+ ******************************************************************************/
+
+/* Table for software algorithm. */
+static uint32_t crc32c_table2[16][256];
+
+/* Flag to indicate if crc32c_init_sw2() has been called. */
+static int crc32c_table2_initialized = 0;
+
+/* Initialize table for software algorithm. */
+static void crc32c_init_sw2(void)
+{
+    for(int i = 0; i < 256; i++)
+    {
+        uint32_t res = (uint32_t)i;
+        for(int t = 0; t < 16; t++) {
+            for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
+            crc32c_table2[t][i] = res;
+        }
+    }
+    crc32c_table2_initialized = 1;
+}
+
+/* Compute CRC32C checksum. */
+uint32_t crc32c_sw2(uint32_t crci, const void *buf, size_t len)
+{
+    const unsigned char *next = buf;
+#ifdef __x86_64__
+    uint64_t crc;
+#else
+    uint32_t crc;
+#endif
+
+    if(!crc32c_table2_initialized)
+        crc32c_init_sw2();
+
+    crc = crci ^ 0xffffffff;
+#ifdef __x86_64__
+    while (len && ((uintptr_t)next & 7) != 0)
+    {
+        crc = crc32c_table2[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        --len;
+    }
+    while (len >= 16)
+    {
+        crc ^= *(uint64_t *)next;
+        uint64_t high = *(uint64_t *)(next + 8);
+        crc = crc32c_table2[15][crc & 0xff]
+            ^ crc32c_table2[14][(crc >> 8) & 0xff]
+            ^ crc32c_table2[13][(crc >> 16) & 0xff]
+            ^ crc32c_table2[12][(crc >> 24) & 0xff]
+            ^ crc32c_table2[11][(crc >> 32) & 0xff]
+            ^ crc32c_table2[10][(crc >> 40) & 0xff]
+            ^ crc32c_table2[9][(crc >> 48) & 0xff]
+            ^ crc32c_table2[8][crc >> 56]
+            ^ crc32c_table2[7][high & 0xff]
+            ^ crc32c_table2[6][(high >> 8) & 0xff]
+            ^ crc32c_table2[5][(high >> 16) & 0xff]
+            ^ crc32c_table2[4][(high >> 24) & 0xff]
+            ^ crc32c_table2[3][(high >> 32) & 0xff]
+            ^ crc32c_table2[2][(high >> 40) & 0xff]
+            ^ crc32c_table2[1][(high >> 48) & 0xff]
+            ^ crc32c_table2[0][high >> 56];
+        next += 16;
+        len -= 16;
+    }
+#else
+    while (len && ((uintptr_t)next & 3) != 0)
+    {
+        crc = crc32c_table2[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        --len;
+    }
+    while (len >= 12)
+    {
+        crc ^= *(uint32_t *)next;
+        uint32_t high = *(uint32_t *)(next + 4);
+        uint32_t high2 = *(uint32_t *)(next + 8);
+        crc = crc32c_table2[11][crc & 0xff]
+            ^ crc32c_table2[10][(crc >> 8) & 0xff]
+            ^ crc32c_table2[9][(crc >> 16) & 0xff]
+            ^ crc32c_table2[8][crc >> 24]
+            ^ crc32c_table2[7][high & 0xff]
+            ^ crc32c_table2[6][(high >> 8) & 0xff]
+            ^ crc32c_table2[5][(high >> 16) & 0xff]
+            ^ crc32c_table2[4][high >> 24]
+            ^ crc32c_table2[3][high2 & 0xff]
+            ^ crc32c_table2[2][(high2 >> 8) & 0xff]
+            ^ crc32c_table2[1][(high2 >> 16) & 0xff]
+            ^ crc32c_table2[0][high2 >> 24];
+        next += 12;
+        len -= 12;
+    }
+#endif
+    while (len)
+    {
+        crc = crc32c_table2[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        --len;
+    }
+    return (uint32_t)crc ^ 0xffffffff;
+}
+
+
+/******************************************************************************
+ *                                                                            *
+ *  Hardware Algorithm - SSE 4.2                                              *
+ *                                                                            *
+ ******************************************************************************/
+
+/* Multiply a matrix times a vector over the Galois field of two elements,
+   GF(2).  Each element is a bit in an unsigned integer.  mat must have at
+   least as many entries as the power of two for most significant one bit in
+   vec. */
+static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec)
+{
+    uint32_t sum;
+
+    sum = 0;
+    while (vec) {
+        if (vec & 1)
+            sum ^= *mat;
+        vec >>= 1;
+        mat++;
+    }
+    return sum;
+}
+
+/* Multiply a matrix by itself over GF(2).  Both mat and square must have 32
+   rows. */
+static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat)
+{
+    int n;
+
+    for (n = 0; n < 32; n++)
+        square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* Construct an operator to apply len zeros to a crc.  len must be a power of
+   two.  If len is not a power of two, then the result is the same as for the
+   largest power of two less than len.  The result for len == 0 is the same as
+   for len == 1.  A variant of this routine could be easily written for any
+   len, but that is not needed for this application. */
+static void crc32c_zeros_op(uint32_t *even, size_t len)
+{
+    int n;
+    uint32_t row;
+    uint32_t odd[32];           /* odd-power-of-two zeros operator */
+
+    /* Put operator for one zero bit in odd. */
+    odd[0] = POLY;              /* CRC32C polynomial */
+    row = 1;
+    for (n = 1; n < 32; n++) {
+        odd[n] = row;
+        row <<= 1;
+    }
+
+    /* Put operator for two zero bits in even. */
+    gf2_matrix_square(even, odd);
+
+    /* Put operator for four zero bits in odd. */
+    gf2_matrix_square(odd, even);
+
+    /* First square will put the operator for one zero byte (eight zero bits),
+       in even -- next square puts operator for two zero bytes in odd, and so
+       on, until len has been rotated down to zero. */
+    do {
+        gf2_matrix_square(even, odd);
+        len >>= 1;
+        if (len == 0)
+            return;
+        gf2_matrix_square(odd, even);
+        len >>= 1;
+    } while (len);
+
+    /* Answer ended up in odd -- copy to even. */
+    for (n = 0; n < 32; n++)
+        even[n] = odd[n];
+}
+
+/* Take a length and build four lookup tables for applying the zeros operator
+   for that length, byte-by-byte on the operand. */
+static void crc32c_zeros(uint32_t zeros[][256], size_t len)
+{
+    uint32_t n;
+    uint32_t op[32];
+
+    crc32c_zeros_op(op, len);
+    for (n = 0; n < 256; n++) {
+        zeros[0][n] = gf2_matrix_times(op, n);
+        zeros[1][n] = gf2_matrix_times(op, n << 8);
+        zeros[2][n] = gf2_matrix_times(op, n << 16);
+        zeros[3][n] = gf2_matrix_times(op, n << 24);
+    }
+}
+
+/* Apply the zeros operator table to crc. */
+static inline uint32_t crc32c_shift(uint32_t zeros[][256], uint32_t crc)
+{
+    return zeros[0][crc & 0xff] ^ zeros[1][(crc >> 8) & 0xff] ^
+           zeros[2][(crc >> 16) & 0xff] ^ zeros[3][crc >> 24];
+}
+
+/* Block sizes for three-way parallel crc computation.  LONG and SHORT must
+   both be powers of two.  The associated string constants must be set
+   accordingly, for use in constructing the assembler instructions. */
+#define LONG 8192
+#define LONGx1 "8192"
+#define LONGx2 "16384"
+#define SHORT 256
+#define SHORTx1 "256"
+#define SHORTx2 "512"
+
+/* Tables for hardware algorithm that shift a crc by LONG and SHORT zeros. */
+static uint32_t crc32c_long[4][256];
+static uint32_t crc32c_short[4][256];
+
+/* Flag to indicate if crc32c_init_hw() has been called. */
+static int crc32c_hw_initialized = 0;
+
+
+
+
+#if defined(__x86_64__)                 /* @RRL: to compile for ARM */
+
+
+
+static void crc32c_init_hw(void)
+{
+    crc32c_zeros(crc32c_long, LONG);
+    crc32c_zeros(crc32c_short, SHORT);
+    crc32c_hw_initialized = 1;
+}
+
+/* Compute CRC32C checksum. */
+uint32_t crc32c_hw(uint32_t crc, const void *buf, size_t len)
+{
+    const unsigned char *next = buf;
+    const unsigned char *end;
+    uint64_t crc0, crc1, crc2;      /* need to be 64 bits for crc32q */
+
+    /* Populate shift tables the first time through. */
+    if (!crc32c_hw_initialized)
+        crc32c_init_hw();
+
+    /* Pre-process the crc. */
+    crc0 = crc ^ 0xffffffff;
+
+    /* Compute the crc for up to seven leading bytes to bring the data pointer
+       to an eight-byte boundary. */
+    while (len && ((uintptr_t)next & 7) != 0) {
+        __asm__("crc32b\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next++;
+        len--;
+    }
+
+    /* Compute the crc on sets of LONG*3 bytes, executing three independent crc
+       instructions, each on LONG bytes -- this is optimized for the Nehalem,
+       Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
+       throughput of one crc per cycle, but a latency of three cycles. */
+    while (len >= LONG*3) {
+        crc1 = 0;
+        crc2 = 0;
+        end = next + LONG;
+        do {
+            __asm__("crc32q\t" "(%3), %0\n\t"
+                    "crc32q\t" LONGx1 "(%3), %1\n\t"
+                    "crc32q\t" LONGx2 "(%3), %2"
+                    : "=r"(crc0), "=r"(crc1), "=r"(crc2)
+                    : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2));
+            next += 8;
+        } while (next < end);
+        crc0 = crc32c_shift(crc32c_long, crc0) ^ crc1;
+        crc0 = crc32c_shift(crc32c_long, crc0) ^ crc2;
+        next += LONG*2;
+        len -= LONG*3;
+    }
+
+    /* Do the same thing, but now on SHORT*3 blocks for the remaining data less
+       than a LONG*3 block. */
+    while (len >= SHORT*3) {
+        crc1 = 0;
+        crc2 = 0;
+        end = next + SHORT;
+        do {
+            __asm__("crc32q\t" "(%3), %0\n\t"
+                    "crc32q\t" SHORTx1 "(%3), %1\n\t"
+                    "crc32q\t" SHORTx2 "(%3), %2"
+                    : "=r"(crc0), "=r"(crc1), "=r"(crc2)
+                    : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2));
+            next += 8;
+        } while (next < end);
+        crc0 = crc32c_shift(crc32c_short, crc0) ^ crc1;
+        crc0 = crc32c_shift(crc32c_short, crc0) ^ crc2;
+        next += SHORT*2;
+        len -= SHORT*3;
+    }
+
+    /* Compute the crc on the remaining eight-byte units less than a SHORT*3
+       block. */
+    end = next + (len - (len & 7));
+    while (next < end) {
+        __asm__("crc32q\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next += 8;
+    }
+    len &= 7;
+
+    /* Compute the crc for up to seven trailing bytes. */
+    while (len) {
+        __asm__("crc32b\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next++;
+        len--;
+    }
+
+    /* Return a post-processed crc. */
+    return (uint32_t)crc0 ^ 0xffffffff;
+}
+
+
+
+#endif /* @RRL: to compile for ARM */
+
+/******************************************************************************
+ *                                                                            *
+ *  Other Functions                                                           *
+ *                                                                            *
+ ******************************************************************************/
+
+/* Variables to store information on hardware support. */
+static int crc32c_hardware_support = 0;
+static int crc32c_hardware_checked = 0;
+
+/* Check for hardware support (SSE 4.2).  Note that this does not check for
+   the existence of the cpuid instruction itself, which was introduced on the
+   486SL in 1992, so this will fail on earlier x86 processors.  cpuid works
+   on all Pentium and later processors. */
+int crc32c_hw_support()
+{
+#if defined(__x86_64__)                 /* @RRL: to compile for ARM */
+    if (!crc32c_hardware_checked) {
+        do {
+            uint32_t eax, ecx;
+            eax = 1;
+            __asm__("cpuid"
+                    : "=c"(ecx)
+                    : "a"(eax)
+                    : "%ebx", "%edx");
+            (crc32c_hardware_support) = (ecx >> 20) & 1;
+        } while (0);
+        crc32c_hardware_checked = 1;
+    }
+    return crc32c_hardware_support;
+#else
+    return  0;
+#endif
+
+}
+
+/* Disable hardware algorithm even if supported by hardware. */
+void crc32c_hw_disable()
+{
+    crc32c_hardware_support = 0;
+    crc32c_hardware_checked = 1;
+}
+
+/* Compute CRC32C checksum. Use hardware algorithm if supported,
+   fall back on software algorithm otherwise. */
+uint32_t crc32c(uint32_t crc, const void *buf, size_t len)
+{
+    return crc32c_hw_support() ? crc32c_hw(crc, buf, len) : crc32c_sw(crc, buf, len);
+}
diff --git a/3rdparty/crc32c_adler/crc32c_adler.h b/3rdparty/crc32c_adler/crc32c_adler.h
new file mode 100755
index 0000000000000000000000000000000000000000..32f3f81e6b5421cac48d9da560919392bb0530ce
--- /dev/null
+++ b/3rdparty/crc32c_adler/crc32c_adler.h
@@ -0,0 +1,61 @@
+/*
+  CRC32C_ADLER -- Computes CRC32C Checksums
+  Version 1.2, Date 05/21/18
+  Copyright (C) 2013 Mark Adler <madler@alumni.caltech.edu>
+  Copyright (C) 2018 Fonic <https://github.com/fonic>
+
+  Provides both a hardware-accelerated algorithm (*) and a software algorithm.
+  Note that this computes CRC32C checksums, not CRC32 (without 'C') checksums
+  used by Ethernet, gzip, etc.
+
+  (*) CRC instruction on Intel SSE 4.2 processors.  SSE 4.2 was first supported
+      by Nehalem processors introduced in November, 2008.
+
+  Version history:
+  1.0  10 Feb 2013  First version
+  1.1   1 Aug 2013  Correct comments on why three crc instructions in parallel
+  1.2  21 May 2018  Add header file, revise hardware support check, eliminate
+                    pthreads, restructure code, revise comments and description
+
+  Version 1.1 by Mark Adler was originally published here:
+  https://stackoverflow.com/a/17646775
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software.  If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#ifndef CRC32C_ADLER_H
+#define CRC32C_ADLER_H
+
+/* Compute CRC32C checksum using software algorithm (1). */
+uint32_t crc32c_sw(uint32_t crci, const void *buf, size_t len);
+
+/* Compute CRC32C checksum using software algorithm (2). */
+uint32_t crc32c_sw2(uint32_t crci, const void *buf, size_t len);
+
+/* Compute CRC32C checksum using hardware algorithm. */
+uint32_t crc32c_hw(uint32_t crc, const void *buf, size_t len);
+
+/* Check if hardware-support (i.e. SSE 4.2) is available. */
+int crc32c_hw_support();
+
+/* Disable hardware algorithm even if supported by hardware. */
+void crc32c_hw_disable();
+
+/* Compute CRC32C checksum. Use hardware algorithm if supported,
+   fall back on software algorithm otherwise. */
+uint32_t crc32c(uint32_t crc, const void *buf, size_t len);
+
+#endif // CRC32C_ADLER_H
diff --git a/3rdparty/crc32c_adler/example-dht.c b/3rdparty/crc32c_adler/example-dht.c
new file mode 100755
index 0000000000000000000000000000000000000000..5bd05b1e6b835eaf6c60d9edd599b0aefa996a27
--- /dev/null
+++ b/3rdparty/crc32c_adler/example-dht.c
@@ -0,0 +1,163 @@
+/*
+  TODO describe example
+*/
+#define _GNU_SOURCE     /* random, srandom*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>      /* open, close etc. */
+#include <string.h>     /* memcpy */
+#include <netinet/in.h> /* AF_INET, INET_ADDRSTRLEN etc. */
+#include <arpa/inet.h>  /* inet_ntop */
+
+#include "crc32c_adler.h"
+
+/* Convert id to hex string of formate used in BEP 42. */
+char id_hex_str[20*2+2+1]; /* 20 bytes x 2 characters + 2 x ' ' + '\0' */
+const char*
+id_to_hex(const unsigned char *id)
+{
+    //const char* hex_chr = "0123456789ABCDEF";
+    const char* hex_chr = "0123456789abcdef";
+    for(int i=0,j=0; i < 20 && j < sizeof(id_hex_str)-2; i++) {
+        id_hex_str[j++] = hex_chr[ (id[i]>>4) & 0x0F ];
+        id_hex_str[j++] = hex_chr[  id[i]     & 0x0F ];
+        if (i == 2 || i == 18) {
+            id_hex_str[j++] = ' ';
+        }
+    }
+    id_hex_str[sizeof(id_hex_str)-1] = '\0';
+    return id_hex_str;
+}
+
+/* Generate node ID from IP address + predefined rand using example algorithm
+   provided in BEP 42.
+
+   Parameters:
+   ip       IPv4 or IPv6 address (network byte order)
+   iplen    number of octets to consider in ip (4 or 8)
+   id       resulting node ID
+   rand     predefined random value */
+void crc32c_id(const uint8_t* ip, int iplen, uint8_t id[20], uint32_t rand)
+{
+    uint8_t v4_mask[] = { 0x03, 0x0f, 0x3f, 0xff };
+    uint8_t v6_mask[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+    uint8_t* mask = iplen == 4 ? v4_mask : v6_mask;
+
+    uint8_t ip_copy[8];
+    memcpy(ip_copy, ip, iplen);
+
+    for (int i = 0; i < iplen; ++i)
+            ip_copy[i] &= mask[i];
+
+    //uint32_t rand = random() & 0xff;
+    uint8_t r = rand & 0x7;
+    ip_copy[0] |= r << 5;
+
+    uint32_t crc = 0;
+    crc = crc32c(crc, ip_copy, iplen);
+
+    /* only take the top 21 bits from crc */
+    id[0] = (crc >> 24) & 0xff;
+    id[1] = (crc >> 16) & 0xff;
+    id[2] = ((crc >> 8) & 0xf8) | (random() & 0x7);
+    for (int i = 3; i < 19; ++i) id[i] = random();
+    id[19] = rand;
+}
+
+/* Find how many bits two ids have in common. */
+int common_bits(const unsigned char *id1, const unsigned char *id2)
+{
+    int i, j;
+    unsigned char xor;
+    for(i = 0; i < 20; i++) {
+        if(id1[i] != id2[i])
+            break;
+    }
+
+    if(i == 20)
+        return 160;
+
+    xor = id1[i] ^ id2[i];
+
+    j = 0;
+    while((xor & 0x80) == 0) {
+        xor <<= 1;
+        j++;
+    }
+
+    return 8 * i + j;
+}
+
+/* Check if a node ID is correct in the sense of BEP 42. */
+int check_id(const uint8_t id1[20], const uint8_t* ip, int iplen, uint32_t rand)
+{
+    /* Generate ID from IP + rand -> id2. */
+    uint8_t id2[20];
+    crc32c_id(ip, iplen, id2, rand);
+
+    /* Compare id1 with id2:
+       - the first 21 bits must match
+       - the last byte must match rand */
+    int cbits = common_bits(id1, id2);
+    if (cbits < 21) {
+        printf("Only the first %i bits match (expected: 21)\n", cbits);
+        return 0;
+    }
+    if (id1[19] != id2[19]) {
+        printf("Last byte does not match (expected: %u, got: %u)\n", id2[19], id1[19]);
+        return 0;
+    }
+    return 1;
+}
+
+/* Main. */
+int main(int argc, char **argv)
+{
+    (void)argc;
+    (void)argv;
+
+    /* Print which CRC32C algorithm is used. */
+    printf("\nUsing %s algorithm.\n\n", crc32c_hw_support() ? "hardware-accelerated (SSE 4.2)" : "software");
+
+    /* Seed random. */
+    int fd = open("/dev/urandom", O_RDONLY);
+    if(fd < 0) {
+        perror("open(random)");
+        exit(1);
+    }
+    unsigned seed;
+    read(fd, &seed, sizeof(seed));
+    srandom(seed);
+    close(fd);
+
+    /* Example IP/rand combinations as used in BEP 42. */
+    uint8_t ip[5][4] = {
+        { 124, 31, 75, 21 },
+        { 21, 75, 31, 124 },
+        { 65, 23, 51, 170 },
+        { 84, 124, 73, 14 },
+        { 43, 213, 53, 83 }
+    };
+    uint32_t rand[] = {
+        1,
+        86,
+        22,
+        65,
+        90
+    };
+    int iplen = 4;
+    uint8_t id[20];
+
+    printf("IP              rand  Node ID                                    Ok?\n");
+    printf("=============== ===== ========================================== ====\n");
+    for (int i = 0; i < 5; ++i) {
+        crc32c_id(ip[i], iplen, id, rand[i]);
+        char ipstr[INET_ADDRSTRLEN];
+        inet_ntop(AF_INET, ip[i], ipstr, sizeof(ipstr));
+        printf("%-15s  %2u   %s %s\n", ipstr, rand[i], id_to_hex(id), (check_id(id, ip[i], 4, rand[i]) ? "yes" : "no"));
+    }
+
+    return 0;
+}
diff --git a/3rdparty/crc32c_adler/example-stdin.c b/3rdparty/crc32c_adler/example-stdin.c
new file mode 100755
index 0000000000000000000000000000000000000000..d64fdb6a8a4408451a2a447de7d7f4abf8a1f33c
--- /dev/null
+++ b/3rdparty/crc32c_adler/example-stdin.c
@@ -0,0 +1,46 @@
+/*
+  TODO describe example
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "crc32c_adler.h"
+
+#define SIZE (262144*3)
+#define CHUNK SIZE
+
+int main(int argc, char **argv)
+{
+    char *buf;
+    ssize_t got;
+    size_t off, n;
+    uint32_t crc;
+
+    (void)argv;
+    crc = 0;
+    buf = malloc(SIZE);
+    if (buf == NULL) {
+        fputs("out of memory", stderr);
+        return 1;
+    }
+    while ((got = read(0, buf, SIZE)) > 0) {
+        off = 0;
+        do {
+            n = (size_t)got - off;
+            if (n > CHUNK)
+                n = CHUNK;
+            crc = argc > 1 ? crc32c_sw(crc, buf + off, n) :
+                             crc32c(crc, buf + off, n);
+            off += n;
+        } while (off < (size_t)got);
+    }
+    free(buf);
+    if (got == -1) {
+        fputs("read error\n", stderr);
+        return 1;
+    }
+    printf("%08x\n", crc);
+    return 0;
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 98e954bf412ee5c076ead5d33f6fbea22013f020..3d922c8554160c15fbfa5f3bce24a387badf9b8f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,11 +66,19 @@ if (CELLFRAME_MODULES MATCHES "test-framework")
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_test)
 endif()
 
+
+    add_subdirectory(3rdparty/crc32c_adler)             # https://github.com/fonic/crc32c_adler
+    include_directories(3rdparty/crc32c_adler)
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} crc32c_adler)
+
+
+
 if(BUILD_TESTS)
     include(cmake/OS_Detection.cmake)
     add_subdirectory(dap-sdk)
 endif()
 
+
 if (BUILD_WITH_ZIP)
     add_subdirectory(3rdparty/libzip)
     include_directories(3rdparty/libzip/lib)
@@ -83,6 +91,7 @@ endif()
 
 add_subdirectory(modules/)
 
+
 add_library(${PROJECT_NAME} STATIC cellframe-sdk.c)
 
 # init libs
@@ -207,6 +216,7 @@ if (CELLFRAME_MODULES MATCHES "srv-stake-pos-delegate")
     #add TARGET_FILE for proper symbols resolving
     #pos_delegate depends on symbols from this libs
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_stake_pos_delegate $<TARGET_FILE:dap_chain_cs_dag_poa> $<TARGET_FILE:dap_chain_cs_block_poa> $<TARGET_FILE:dap_chain_cs_dag> $<TARGET_FILE:dap_chain_cs_blocks>)
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} crc32c_adler)
 endif()
 
 # Enable service for dynamic modules
@@ -217,7 +227,7 @@ endif()
 
 if (WIN32)
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} kernel32 user32 shell32 winmm gdi32 advapi32
-					 ole32 version imm32 oleaut32 ws2_32 ntdll psapi 
+					 ole32 version imm32 oleaut32 ws2_32 ntdll psapi
                                          shlwapi bcrypt crypt32 secur32 userenv) #mqrt)
 endif()
 
@@ -225,7 +235,11 @@ if (DARWIN)
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} bz2)
 endif()
 
-target_link_libraries(${PROJECT_NAME} ${CELLFRAME_LIBS})
+set(CELLFRAME_LIBS ${CELLFRAME_LIBS} crc32c_adler)
+
+
+
+target_link_libraries(${PROJECT_NAME} ${CELLFRAME_LIBS} crc32c_adler)
 #if(BUILD_DAP_TESTS)
 #    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/test/main_test.py
 #            DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 04404537e8b3dd62802b9b0dd0b06d7b98d4cc9a..11acd29739b4d6227de7b2f3013c05168cd52c02 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -118,8 +118,12 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
                                         "? [<command>]\n"
                                         "\tObtain help for <command> or get the total list of the commands\n"
                                         );
-    dap_cli_server_cmd_add("wallet", com_tx_wallet, "Wallet operations",
-            "wallet {new -w <wallet_name> [-sign <sign_type>] [-restore <hex_value>] [-net <net_name>] [-force] | list | info {-addr <addr> | -w <wallet_name>} -net <net_name>}\n");
+    dap_cli_server_cmd_add ("wallet", com_tx_wallet, "Wallet operations",
+                "wallet list\n"
+                "wallet new -w <wallet_name> [-sign <sign_type>] [-restore <hex_value>] [-net <net_name>] [-force] [-password <password>] [-restore <hash>]\n"
+                "wallet info {-addr <addr> | -w <wallet_name>} -net <net_name>\n"
+                "wallet activate -w <wallet_name> -password <password> [-ttl <password_ttl_in_minutes>]\n"
+                "wallet deactivate -w <wallet_name> -password <password>\n");
 
     // Token commands
     dap_cli_server_cmd_add ("token_update", com_token_update, "Token update",
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index 462e95f2c1415d656230724f1e8f9e06952ae584..a8ce008a4056a1edc585a46803473aaf9252ede3 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -1696,26 +1696,29 @@ int com_help(int argc, char ** argv, char **str_reply)
  * @param str_reply
  * @return int
  */
+#if 0
 int com_tx_wallet(int argc, char ** argv, char **str_reply)
 {
-    const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
-    // Get address of wallet
-    enum {
-        CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO
-    };
-    int arg_index = 1;
-    int cmd_num = CMD_NONE;
+const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
+enum { CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO, CMD_WALLET_ACTIVATE, CMD_WALLET_DEACTIVATE };
+int l_arg_index = 1, l_rc, cmd_num = CMD_NONE;
+char    l_buf[1024];
+
+
     // find  add parameter ('alias' or 'handshake')
-    if(dap_cli_server_cmd_find_option_val(argv, arg_index, min(argc, arg_index + 1), "new", NULL)) {
+    if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "new", NULL))
         cmd_num = CMD_WALLET_NEW;
-    }
-    else if(dap_cli_server_cmd_find_option_val(argv, arg_index, min(argc, arg_index + 1), "list", NULL)) {
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "list", NULL))
         cmd_num = CMD_WALLET_LIST;
-    }
-    else if(dap_cli_server_cmd_find_option_val(argv, arg_index, min(argc, arg_index + 1), "info", NULL)) {
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "info", NULL))
         cmd_num = CMD_WALLET_INFO;
-    }
-    arg_index++;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "activate", NULL))
+        cmd_num = CMD_WALLET_ACTIVATE;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "deactivate", NULL))
+        cmd_num = CMD_WALLET_DEACTIVATE;
+
+    l_arg_index++;
+
     if(cmd_num == CMD_NONE) {
         dap_cli_server_cmd_set_reply_text(str_reply,
                 "Format of command: wallet [new -w <wallet_name> | list | info [<-addr <addr>]|[-w <wallet_name> -net <net_name>]");
@@ -1930,6 +1933,319 @@ int com_tx_wallet(int argc, char ** argv, char **str_reply)
     *str_reply = dap_string_free(l_string_ret, false);
     return 0;
 }
+#endif
+
+int com_tx_wallet(int argc, char ** argv, char **str_reply)
+{
+const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
+enum { CMD_NONE, CMD_WALLET_NEW, CMD_WALLET_LIST, CMD_WALLET_INFO, CMD_WALLET_ACTIVATE, CMD_WALLET_DEACTIVATE };
+int l_arg_index = 1, l_rc, cmd_num = CMD_NONE;
+char    l_buf[1024];
+
+
+    // find  add parameter ('alias' or 'handshake')
+    if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "new", NULL))
+        cmd_num = CMD_WALLET_NEW;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "list", NULL))
+        cmd_num = CMD_WALLET_LIST;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "info", NULL))
+        cmd_num = CMD_WALLET_INFO;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "activate", NULL))
+        cmd_num = CMD_WALLET_ACTIVATE;
+    else if(dap_cli_server_cmd_find_option_val(argv, l_arg_index, min(argc, l_arg_index + 1), "deactivate", NULL))
+        cmd_num = CMD_WALLET_DEACTIVATE;
+
+    l_arg_index++;
+
+    if(cmd_num == CMD_NONE) {
+        dap_cli_server_cmd_set_reply_text (str_reply,
+                "Format of command: wallet {new -w <wallet_name> | list | info [-addr <addr>]|[-w <wallet_name> -net <net_name>]}");
+        return -1;
+    }
+
+    const char *l_addr_str = NULL, *l_wallet_name = NULL, *l_net_name = NULL, *l_sign_type_str = NULL, *l_restore_str = NULL,
+            *l_pass_str = NULL, *l_ttl_str = NULL;
+
+    // find wallet addr
+    dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-addr", &l_addr_str);
+    dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-w", &l_wallet_name);
+    dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-net", &l_net_name);
+
+
+
+    dap_chain_net_t * l_net = l_net_name ? dap_chain_net_by_name( l_net_name) : NULL;
+
+    dap_string_t *l_string_ret = dap_string_new(NULL);
+
+
+    switch (cmd_num)
+    {
+        case CMD_WALLET_ACTIVATE:
+        case CMD_WALLET_DEACTIVATE:
+            dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-password", &l_pass_str);
+            dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-ttl", &l_ttl_str);
+
+
+            if( !l_wallet_name )
+                return  dap_cli_server_cmd_set_reply_text(str_reply, "Wallet name option <-w>  not defined"), -EINVAL;
+
+            if( !l_pass_str )
+                return  dap_cli_server_cmd_set_reply_text(str_reply, "Wallet password option <-password>  not defined"), -EINVAL;
+
+            if ( l_ttl_str )
+                l_rc = strtoul(l_ttl_str, NULL, 10);
+            else    l_rc = 60;
+                l_rc = l_rc ? l_rc : 60;
+
+            if ( cmd_num == CMD_WALLET_ACTIVATE )
+                    l_rc = dap_chain_wallet_activate   (l_wallet_name, strlen(l_wallet_name), l_pass_str, strlen(l_pass_str), l_rc );
+            else    l_rc = dap_chain_wallet_deactivate (l_wallet_name, strlen(l_wallet_name), l_pass_str, strlen(l_pass_str) );
+
+            if ( !l_rc )
+                    dap_string_append_printf(l_string_ret, "Wallet: %s is %sactivated\n",
+                        l_wallet_name, cmd_num == CMD_WALLET_ACTIVATE ? "" : "de");
+            else
+            {
+                switch ( l_rc )
+                {
+                    case    -EBUSY:
+                        strcpy(l_buf, "already activated");
+                        break;
+
+                    case    -EINVAL:
+                        strcpy(l_buf, "wrong password");
+                        break;
+
+
+                    default:
+                        strerror_r(l_rc, l_buf, sizeof(l_buf) - 1 );
+                        break;
+                }
+
+                dap_string_append_printf(l_string_ret, "Wallet: %s  %sactivation error, errno=%d (%s)\n",
+                        l_wallet_name, cmd_num == CMD_WALLET_ACTIVATE ? "" : "de", l_rc, l_buf );
+            }
+
+            break;
+
+
+        // new wallet
+        case CMD_WALLET_NEW: {
+            dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-password", &l_pass_str);
+            dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-sign", &l_sign_type_str);
+            dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-restore", &l_restore_str);
+            // rewrite existing wallet
+            int l_is_force = dap_cli_server_cmd_find_option_val(argv, l_arg_index, argc, "-force", NULL);
+
+            if(!l_wallet_name) {
+                dap_cli_server_cmd_set_reply_text(str_reply, "Wallet name option <-w>  not defined");
+                return -1;
+            }
+            // Check if wallet name has only digits and English letter
+            if (!dap_isstralnum(l_wallet_name)){
+                dap_cli_server_cmd_set_reply_text(str_reply, "Wallet name must contains digits and aplhabetical symbols");
+                return -1;
+            }
+
+            // check wallet existence
+            if (!l_is_force) {
+                char *l_file_name = dap_strdup_printf("%s/%s.dwallet", c_wallets_path, l_wallet_name);
+                FILE *l_exists = fopen(l_file_name, "rb");
+                DAP_DELETE(l_file_name);
+                if (l_exists) {
+                    dap_cli_server_cmd_set_reply_text(str_reply, "Wallet %s already exists", l_wallet_name);
+                    fclose(l_exists);
+                    return -1;
+                }
+            }
+
+            dap_sign_type_t l_sign_type;
+            if (!l_sign_type_str) {
+                l_sign_type.type = SIG_TYPE_DILITHIUM;
+                l_sign_type_str = dap_sign_type_to_str(l_sign_type);
+            } else {
+                l_sign_type = dap_sign_type_from_str(l_sign_type_str);
+                if (l_sign_type.type == SIG_TYPE_NULL){
+                    dap_cli_server_cmd_set_reply_text(str_reply, "Unknown signature type");
+                    return -1;
+                }
+            }
+
+            //
+            // Check unsupported tesla algorithm
+            //
+
+            if (l_sign_type.type == SIG_TYPE_TESLA)
+                return  dap_cli_server_cmd_set_reply_text(str_reply, "Tesla algorithm is no longer supported, please, use another variant"), -1;
+
+            uint8_t *l_seed = NULL;
+            size_t l_seed_size = 0, l_restore_str_size = dap_strlen(l_restore_str);
+
+            if(l_restore_str && l_restore_str_size > 2 && !dap_strncmp(l_restore_str, "0x", 2)) {
+                l_seed_size = (l_restore_str_size - 2) / 2;
+                l_seed = DAP_NEW_SIZE(uint8_t, l_seed_size);
+                if(!dap_hex2bin(l_seed, l_restore_str + 2, l_restore_str_size - 2)){
+                    DAP_DELETE(l_seed);
+                    l_seed = NULL;
+                    l_seed_size = 0;
+                    dap_cli_server_cmd_set_reply_text(str_reply, "Restored hash is invalid, wallet is not created");
+                    return -1;
+                }
+            }
+            // Creates new wallet
+            dap_chain_wallet_t *l_wallet = dap_chain_wallet_create_with_seed(l_wallet_name, c_wallets_path, l_sign_type,
+                    l_seed, l_seed_size, l_pass_str);
+
+            if (!l_wallet)
+                return  dap_cli_server_cmd_set_reply_text(str_reply, "Wallet is not created because of internal error"), -1;
+
+            dap_chain_addr_t *l_addr = l_net? dap_chain_wallet_get_addr(l_wallet,l_net->pub.id ) : NULL;
+
+            char *l_addr_str = l_addr? dap_chain_addr_to_str(l_addr) : NULL;
+            dap_string_append_printf(l_string_ret, "Wallet: %s (type=%s) successfully created\n", l_wallet->name, l_sign_type_str);
+            if ( l_addr_str ) {
+                dap_string_append_printf(l_string_ret, "new address %s", l_addr_str);
+                DAP_DELETE(l_addr_str);
+            }
+            dap_chain_wallet_close(l_wallet);
+        }
+        break;
+
+
+        // wallet list
+        case CMD_WALLET_LIST:
+        {
+            DIR * l_dir = opendir(c_wallets_path);
+            if(l_dir) {
+                struct dirent * l_dir_entry;
+
+                while( (l_dir_entry = readdir(l_dir)) )
+                {
+                    const char *l_file_name = l_dir_entry->d_name;
+                    size_t l_file_name_len = (l_file_name) ? strlen(l_file_name) : 0;
+
+                    if ( (l_file_name_len > 8) && (!strcmp(l_file_name + l_file_name_len - 8, ".dwallet")) )
+                    {
+
+                        char l_file_path_tmp[MAX_PATH] = {0};
+                        dap_snprintf(l_file_path_tmp, sizeof(l_file_path_tmp) - 1, "%s/%s", c_wallets_path, l_file_name);
+
+                        dap_chain_wallet_t *l_wallet = dap_chain_wallet_open(l_file_name, c_wallets_path);
+
+                        if (l_wallet)
+                        {
+                            dap_chain_addr_t *l_addr = l_net? dap_chain_wallet_get_addr(l_wallet, l_net->pub.id) : NULL;
+                            char *l_addr_str = dap_chain_addr_to_str(l_addr);
+
+                            dap_string_append_printf(l_string_ret, "Wallet: %s%s\n", l_wallet->name,
+                                (l_wallet->flags & DAP_WALLET$M_FL_ACTIVE) ? " (Active)" : "");
+
+                            if (l_addr_str)
+                            {
+                                dap_string_append_printf(l_string_ret, "addr: %s\n", (l_addr_str) ? l_addr_str : "-");
+                                DAP_DELETE(l_addr_str);
+                            }
+
+                            dap_chain_wallet_close(l_wallet);
+
+                        } else dap_string_append_printf(l_string_ret, "Wallet: %.*s (non-Active)\n", (int) l_file_name_len - 8, l_file_name);
+                    }
+                }
+                closedir(l_dir);
+            }
+        }
+        break;
+
+        // wallet info
+        case CMD_WALLET_INFO: {
+            dap_chain_wallet_t *l_wallet = NULL;
+            dap_chain_addr_t *l_addr = NULL;
+
+            if(l_wallet_name) {
+                l_wallet = dap_chain_wallet_open(l_wallet_name, c_wallets_path);
+                if ( l_net )
+                    l_addr = (dap_chain_addr_t *) dap_chain_wallet_get_addr(l_wallet, l_net->pub.id );
+            }
+            if(!l_addr && l_addr_str)
+                l_addr = dap_chain_addr_from_str(l_addr_str);
+
+            dap_ledger_t *l_ledger = dap_chain_ledger_by_net_name((const char *) l_net_name);
+            if(!l_net_name && !l_addr ) {
+                dap_cli_server_cmd_set_reply_text(str_reply, "Subcommand info requires parameter '-net'");
+                return -1;
+            }
+            else if (! l_addr){
+                if((l_ledger = dap_chain_ledger_by_net_name(l_net_name)) == NULL) {
+                    dap_cli_server_cmd_set_reply_text(str_reply, "Not found net by name '%s'", l_net_name);
+                    return -1;
+                }
+            }else{
+                l_net = dap_chain_net_by_id(l_addr->net_id);
+                if (l_net){
+                l_ledger = l_net->pub.ledger;
+                    l_net_name = l_net->pub.name;
+                }else{
+                    dap_cli_server_cmd_set_reply_text(str_reply, "Can't find network id 0x%08X from address %s", l_addr->net_id.uint64,
+                                                      l_addr_str);
+                    return -1;
+
+                }
+            }
+
+            if(l_addr) {
+                char *l_addr_str = dap_chain_addr_to_str((dap_chain_addr_t*) l_addr);
+                if(l_wallet)
+                    dap_string_append_printf(l_string_ret, "wallet: %s\n", l_wallet->name);
+                dap_string_append_printf(l_string_ret, "addr: %s\n", (l_addr_str) ? l_addr_str : "-");
+                dap_string_append_printf(l_string_ret, "network: %s\n", (l_net_name ) ? l_net_name : "-");
+
+                size_t l_l_addr_tokens_size = 0;
+                char **l_l_addr_tokens = NULL;
+                dap_chain_ledger_addr_get_token_ticker_all(l_ledger, l_addr, &l_l_addr_tokens, &l_l_addr_tokens_size);
+                if(l_l_addr_tokens_size > 0)
+                    dap_string_append_printf(l_string_ret, "balance:\n");
+                else
+                    dap_string_append_printf(l_string_ret, "balance: 0");
+
+                for(size_t i = 0; i < l_l_addr_tokens_size; i++) {
+                    if(l_l_addr_tokens[i]) {
+                        uint256_t l_balance = dap_chain_ledger_calc_balance(l_ledger, l_addr, l_l_addr_tokens[i]);
+                        char *l_balance_coins = dap_chain_balance_to_coins(l_balance);
+                        char *l_balance_datoshi = dap_chain_balance_print(l_balance);
+                        dap_string_append_printf(l_string_ret, "\t%s (%s) %s\n", l_balance_coins,
+                                l_balance_datoshi, l_l_addr_tokens[i]);
+                        if(i < l_l_addr_tokens_size - 1)
+                            dap_string_append_printf(l_string_ret, "\n");
+                        DAP_DELETE(l_balance_coins);
+                        DAP_DELETE(l_balance_datoshi);
+
+                    }
+                    DAP_DELETE(l_l_addr_tokens[i]);
+                }
+                DAP_DELETE(l_l_addr_tokens);
+                DAP_DELETE(l_addr_str);
+                if(l_wallet)
+                    dap_chain_wallet_close(l_wallet);
+            }
+            else {
+                if(l_wallet)
+                    dap_chain_wallet_close(l_wallet);
+
+                dap_string_free(l_string_ret, true);
+                dap_cli_server_cmd_set_reply_text(str_reply, "Wallet not found");
+                return -1;
+            }
+        }
+        break;
+    }
+
+    *str_reply = dap_string_free(l_string_ret, false);
+    return 0;
+}
+
+
+
+
 
 /**
  * @brief s_values_parse_net_chain
@@ -2031,7 +2347,7 @@ static dap_chain_datum_token_t * s_sign_cert_in_cycle(dap_cert_t ** l_certs, dap
     }
 
     size_t l_tsd_size = 0;
-    if ((l_datum_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL) 
+    if ((l_datum_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_PRIVATE_DECL)
         ||  (l_datum_token->type == DAP_CHAIN_DATUM_TOKEN_TYPE_NATIVE_DECL))
         l_tsd_size = l_datum_token->header_native_decl.tsd_total_size;
 
diff --git a/modules/wallet/CMakeLists.txt b/modules/wallet/CMakeLists.txt
index 4feeb32d5872d7fa6d33d657452044dd6cb4376a..b6219a6e2485b05cdb583844bf63d864e40bfc70 100644
--- a/modules/wallet/CMakeLists.txt
+++ b/modules/wallet/CMakeLists.txt
@@ -1,12 +1,12 @@
 cmake_minimum_required(VERSION 3.10)
 project (dap_chain_wallet)
-  
+
 file(GLOB DAP_CHAIN_WALLET_SRCS *.c)
 file(GLOB DAP_CHAIN_WALLET_HEADERS include/*.h)
 
 add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_WALLET_SRCS} ${DAP_CHAIN_WALLET_HEADERS})
 
-target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_chain dap_chain_net)
+target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_chain dap_chain_net crc32c_adler)
 
 target_include_directories(${PROJECT_NAME} INTERFACE .)
 target_include_directories(${PROJECT_NAME} PUBLIC include)
diff --git a/modules/wallet/dap_chain_wallet.c b/modules/wallet/dap_chain_wallet.c
index d51811c615965d35230fe12fb3579a1422212d2b..fd7e2aceff5130323258b323293a7cba3223ec00 100644
--- a/modules/wallet/dap_chain_wallet.c
+++ b/modules/wallet/dap_chain_wallet.c
@@ -20,6 +20,11 @@
 
     You should have received a copy of the GNU General Public License
     along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+
+    MODIFICATION HISTORY:
+
+    27-APR-2021 RRL Added password protected wallet support
+
 */
 
 #include <stdlib.h>
@@ -35,6 +40,7 @@
 #ifdef DAP_OS_UNIX
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
 #endif
 
 #ifdef WIN32
@@ -46,6 +52,8 @@
 #endif
 
 #include <pthread.h>
+#include "crc32c_adler.h"
+
 
 #include "dap_common.h"
 #include "dap_strfuncs.h"
@@ -53,42 +61,294 @@
 #include "dap_cert_file.h"
 #include "dap_chain_wallet.h"
 #include "dap_chain_wallet_internal.h"
+#include "dap_enc_key.h"
 
 #define LOG_TAG "dap_chain_wallet"
 
+                                                                            /* An argument for open()/create() */
+static const mode_t s_fileprot =  ( S_IREAD | S_IWRITE) | (S_IREAD >> 3) | (S_IREAD >> 6) ;
+static char s_wallet_ext [] = ".dwallet";
+
+
+static  pthread_rwlock_t s_wallet_n_pass_lock = PTHREAD_RWLOCK_INITIALIZER; /* Coordinate access to the hash-table */
+static  dap_chain_wallet_n_pass_t   *s_wallet_n_pass;                       /* A hash table to keep passwords for wallets */
+
+
+
+/*	CRC32-C	*/
+#define     CRC32C_INIT    0xEDB88320
+
+
+/*
+ *  DESCRIPTION: Add/update a record for wallet into the internaly used table of name/password pair.
+ *      Thhose records are supposed to be used for operations with the password-protected wallets.
+ *
+ *  INPUTS:
+ *      a_name:     A name of the wallet
+ *      a_name_len: A length of the wallet's name
+ *      a_pass:     A password string
+ *      a_pass_len: A length of the password string
+ *      a_ttl:      A time  to live of the wallet's context, minutes
+ *
+ *  IMPLICITE OUTPUTS:
+ *      s_wallet_n_pass
+ *
+ *  RETURNS:
+ *      0   - Success
+ *      <0  -   <errno>
+ */
+
+int     dap_chain_wallet_activate   (
+                    const   char    *a_name,
+                        ssize_t      a_name_len,
+                    const   char    *a_pass,
+                        ssize_t      a_pass_len,
+                        unsigned     a_ttl
+                                    )
+{
+int     l_rc, l_rc2;
+dap_chain_wallet_n_pass_t   l_rec = {0}, *l_prec;
+dap_chain_wallet_t  *l_wallet;
+char *c_wallets_path;
+
+    /* Sanity checks ... */
+    if ( a_name_len > DAP_WALLET$SZ_NAME )
+        return  log_it(L_ERROR, "Wallet's name is too long (%d > %d)",  a_name_len, DAP_WALLET$SZ_NAME), -EINVAL;
+
+    if ( a_pass_len > DAP_WALLET$SZ_PASS )
+        return  log_it(L_ERROR, "Wallet's password is too long (%d > %d)",  a_pass_len, DAP_WALLET$SZ_PASS), -EINVAL;
+
+
+    memcpy(l_rec.name, a_name, l_rec.name_len = a_name_len);            /* Prefill local record fields */
+    memcpy(l_rec.pass, a_pass, l_rec.pass_len = a_pass_len);
+
+    if ( (l_rc2 = pthread_rwlock_wrlock(&s_wallet_n_pass_lock)) )        /* Lock for WR access */
+        return  log_it(L_ERROR, "Error locking Wallet table, errno=%d", l_rc2), -l_rc2;
+
+    HASH_FIND_STR(s_wallet_n_pass, a_name,  l_prec);                    /* Check for existen record */
+
+
+    l_rc = 0;
+
+    if ( !l_prec )
+    {
+        l_prec  = DAP_NEW_Z(dap_chain_wallet_n_pass_t);                 /* Get memory for new record */
+        *l_prec = l_rec;                                                /* Fill it by data */
+        HASH_ADD_STR(s_wallet_n_pass, name, l_prec);                    /* Add into the hash-table */
+    }
+    else {
+        if ( !l_prec->pass_len )                                        /* Password field is empty ? */
+            memcpy(l_prec->pass, a_pass, l_prec->pass_len = a_pass_len);/* Update password with new one */
+
+        else l_rc = -EBUSY, log_it(L_ERROR, "Wallet has been activated, do deactivation first");
+    }
+
+
+    clock_gettime(CLOCK_REALTIME, &l_prec->exptm);
+    l_prec->exptm.tv_sec += (a_ttl * 60);                               /* Compute context expiration time */
+
+
+    if ( (l_rc2 = pthread_rwlock_unlock(&s_wallet_n_pass_lock)) )        /* Release lock */
+        log_it(L_ERROR, "Error unlocking Wallet table, errno=%d", l_rc2);
+
+
+    /*
+     * Check password by open/close BMF Wallet file
+    */
+    if ( !(c_wallets_path = (char *) dap_chain_wallet_get_path(g_config)) ) /* No path to wallets - nothing to do */
+    {
+        memset(l_prec->pass, 0, l_prec->pass_len), l_prec->pass_len = 0;
+        return  log_it(L_ERROR, "Wallet's path has been not configured"), -EINVAL;
+    }
+
+    if ( !(l_wallet = dap_chain_wallet_open (a_name, c_wallets_path)) )
+    {
+        memset(l_prec->pass, 0, l_prec->pass_len), l_prec->pass_len = 0;
+        return  log_it(L_ERROR, "Wallet's password is invalid"), -EINVAL;
+    }
+
+    dap_chain_wallet_close( l_wallet);
+
+    return  l_rc;
+}
+
+/*
+ *  DESCRIPTIOB: Lookup and retrieve password for a given wallet. A buffer for a_pass should be enough
+ *      to accept password string up to DAP_WALLET$SZ_PASS octets
+ *
+ *  INPUTS:
+ *      a_name:     A name of the wallet
+ *      a_name_len: A length of the wallet's name
+        a_pass_len: A size of the buffer to accept password
+ *
+ *  IMPLICITE INPUTS:
+ *      s_wallet_n_pass
+ *
+ *  OUTPUTS:
+ *      a_pass:     A password string
+ *      a_pass_len: A length of the password string
+ *
+ *  RETURNS:
+ *      0   - Success, <a_pass> and a_pass_len contains actual data
+ *      <0  -   <errno>
+ */
+
+int     s_dap_chain_wallet_pass   (
+                    const   char    *a_name,
+                        ssize_t      a_name_len,
+                            char    *a_pass,
+                        ssize_t     *a_pass_len
+                                    )
+{
+int     l_rc;
+dap_chain_wallet_n_pass_t   *l_prec;
+struct timespec l_now;
+
+    /* Sanity checks ... */
+    if ( a_name_len > DAP_WALLET$SZ_NAME )
+        return  log_it(L_ERROR, "Wallet's name is too long (%d > %d)",  a_name_len, DAP_WALLET$SZ_NAME), -EINVAL;
+
+    if ( *a_pass_len < DAP_WALLET$SZ_PASS )
+        return  log_it(L_ERROR, "Wallet's buffer for password is too small (%d < %d)",  *a_pass_len, DAP_WALLET$SZ_PASS), -EINVAL;
+
+
+    clock_gettime(CLOCK_REALTIME, &l_now);
+
+
+    if ( (l_rc = pthread_rwlock_rdlock(&s_wallet_n_pass_lock)) )        /* Lock for RD access */
+        return  log_it(L_ERROR, "Error locking Wallet table, errno=%d", l_rc), -l_rc;
+
+    HASH_FIND_STR(s_wallet_n_pass, a_name, l_prec);                     /* Check for existen record */
+
+
+    if (l_prec && (l_now.tv_sec > l_prec->exptm.tv_sec) )               /* Record is expired ? */
+    {
+                                                                        /* Reset password field */
+        memset(l_prec->pass, l_prec->pass_len = 0, sizeof(l_prec->pass));
+        l_prec = NULL; //log_it(L_ERROR, "Wallet's credential has been expired, need re-Activation ");
+    }
+    else if ( l_prec && !l_prec->pass_len )                             /* Is record has been deactivated ? */
+        l_prec = NULL; // log_it(L_ERROR, "Wallet's credential has been zeroed, need re-Activation ");
+    else if ( l_prec )                                                  /* Store password to given buffer */
+        memcpy(a_pass, l_prec->pass, *a_pass_len = l_prec->pass_len);
+
+    if ( (l_rc = pthread_rwlock_unlock(&s_wallet_n_pass_lock)) )        /* Release lock */
+        log_it(L_ERROR, "Error locking Wallet table, errno=%d", l_rc);
+
+    return  l_prec ? 0 : -ENOENT;
+}
+
+
+
+/*
+ *  DESCRIPTION: Deactivate a data for the wallet's name & password pair. For existen record just clearing password field.
+ *      Use given password to additional verification. We don't remove record from the hash table - only reset to zero the password field !
+ *
+ *  INPUTS:
+ *      a_name:     A name of the wallet
+ *      a_name_len: A length of the wallet's name
+ *      a_pass:     A password string
+ *      a_pass_len: A length of the password string
+ *
+ *  IMPLICITE OUTPUTS:
+ *      s_wallet_n_pass
+ *
+ *  RETURNS:
+ *      0   - Success
+ *      <0  -   <errno>
+ */
+int     dap_chain_wallet_deactivate   (
+                    const   char    *a_name,
+                        ssize_t      a_name_len,
+                    const   char    *a_pass,
+                        ssize_t      a_pass_len
+                                    )
+{
+int     l_rc, l_rc2;
+dap_chain_wallet_n_pass_t   *l_prec;
+
+    if ( a_name_len > DAP_WALLET$SZ_NAME )
+        return  log_it(L_ERROR, "Wallet's name is too long (%d > %d)",  a_name_len, DAP_WALLET$SZ_NAME), -EINVAL;
+
+    if ( (l_rc = pthread_rwlock_wrlock(&s_wallet_n_pass_lock)) )        /* Lock for WR access */
+        return  log_it(L_ERROR, "Error locking Wallet table, errno=%d", l_rc), -l_rc;
+
+    l_rc = -ENOENT;
+
+    HASH_FIND_STR(s_wallet_n_pass, a_name, l_prec);                     /* Check for existen record */
+
+    if ( l_prec )
+    {
+        if ( !l_prec->pass_len )                                        /* Password is zero - has been reset probably */
+            log_it(L_WARNING, "The Wallet %.*s is not active", a_name_len, a_name);
+
+        else if ( (l_prec->pass_len != a_pass_len)                      /* Check that passwords is equivalent */
+             || memcmp(l_prec->pass, a_pass, l_prec->pass_len) )
+            l_rc = -EINVAL, log_it(L_ERROR, "Wallet's password does not match");
+
+        else    l_rc = 0, memset(l_prec->pass, l_prec->pass_len = 0, sizeof(l_prec->pass));
+    }
+
+    if ( (l_rc2 = pthread_rwlock_unlock(&s_wallet_n_pass_lock)) )       /* Release lock */
+        log_it(L_ERROR, "Error unlocking Wallet table, errno=%d", l_rc2);
+
+    return  l_rc;
+}
+
+
+
+
+
+
+
+
+
+
 /**
  * @brief dap_chain_wallet_init
  * @return
  */
 int dap_chain_wallet_init(void)
 {
-    // load certificates from existing wallets
-    const char *c_wallets_path = dap_chain_wallet_get_path(g_config);
-    if(c_wallets_path) {
-        DIR * l_dir = opendir(c_wallets_path);
-        if (l_dir) {
-            struct dirent * l_dir_entry;
-            while((l_dir_entry = readdir(l_dir)) != NULL) {
-                const char *l_file_name = l_dir_entry->d_name;
-                size_t l_file_name_len = (l_file_name) ? strlen(l_file_name) : 0;
-                if((l_file_name_len > 8) && (strcmp(l_file_name + l_file_name_len - 8, ".dwallet") == 0)) {
-                    char l_file_path_tmp[MAX_PATH] = {'\0'};
-                    dap_sprintf(l_file_path_tmp, "%s/%s", c_wallets_path, l_file_name);
-                    dap_chain_wallet_t *l_wallet = dap_chain_wallet_open_file(l_file_path_tmp);
-                    if(l_wallet) {
-                        dap_chain_wallet_close(l_wallet);
-                    }
-                }
-            }
-            closedir(l_dir);
-        } else {
+char *c_wallets_path, l_fspec[MAX_PATH] = {0};
+DIR * l_dir;
+struct dirent * l_dir_entry;
+dap_chain_wallet_t *l_wallet;
+size_t l_len;
+
+    if ( !(c_wallets_path = (char *) dap_chain_wallet_get_path(g_config)) ) /* No path to wallets - nothing to do */
+        return 0;
+
+    if ( !(l_dir = opendir(c_wallets_path)) )                               /* Path is not exist ? Create the dir and exit */
+    {
 #ifdef _WIN32
-            mkdir(c_wallets_path);
+        mkdir(c_wallets_path);
 #else
-            mkdir(c_wallets_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+        mkdir(c_wallets_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
 #endif
+        return  0;
+    }
+
+    /*
+     * Load certificates from existing no-password-protected (!!!) wallets
+     */
+    while( (l_dir_entry = readdir(l_dir)))
+    {
+        if ( l_dir_entry->d_type !=  DT_REG )                           /* Skip unrelated entries */
+            continue;
+
+        l_len = strlen(l_dir_entry->d_name);                            /* Check for *.dwallet */
+
+        if ( (l_len > 8) && (strcmp(l_dir_entry->d_name + l_len - (sizeof(s_wallet_ext) - 1), s_wallet_ext) == 0) )
+        {
+            dap_snprintf(l_fspec, sizeof(l_fspec) - 1, "%s/%s", c_wallets_path, l_dir_entry->d_name);
+
+            if ( (l_wallet = dap_chain_wallet_open_file(l_fspec, NULL)) )
+                dap_chain_wallet_close(l_wallet);
         }
     }
+
+    closedir(l_dir);
     return 0;
 }
 
@@ -105,14 +365,21 @@ void dap_chain_wallet_deinit(void)
  * @param[in] a_config Configuration
  * @return wallets path or NULL if error
  */
+static char s_wallets_path[MAX_PATH];
+
 const char* dap_chain_wallet_get_path(dap_config_t * a_config)
 {
-    static char l_wallets_path[MAX_PATH];
-    if (strlen(l_wallets_path) > 3)
-        goto RET;
-    dap_sprintf(l_wallets_path, "%s", dap_config_get_item_str(g_config, "resources", "wallets_path"));
-RET:
-    return l_wallets_path;
+char *l_cp;
+
+    if ( s_wallets_path[0] )                                                /* Is the path to the wallet's store has been defined ? */
+        return  s_wallets_path;                                             /* Fine, just return existen value */
+
+                                                                            /* Retrieve Wallet's store path from config */
+    if ( !(l_cp = (char *) dap_config_get_item_str(g_config, "resources", "wallets_path")) )
+        return  log_it(L_WARNING, "No path to wallet's store has been defined"), s_wallets_path;
+
+
+    return  strncpy(s_wallets_path, l_cp, sizeof(s_wallets_path) - 1 );     /* Make local copy , return it to caller */
 }
 
 /**
@@ -124,31 +391,54 @@ RET:
  * @details Creates new wallet
  * @return Wallet, new wallet or NULL if errors
  */
-dap_chain_wallet_t * dap_chain_wallet_create_with_seed(const char * a_wallet_name, const char * a_wallets_path,
-        dap_sign_type_t a_sig_type, const void* a_seed, size_t a_seed_size)
+dap_chain_wallet_t * dap_chain_wallet_create_with_seed (
+                    const char * a_wallet_name,
+                    const char * a_wallets_path,
+                    dap_sign_type_t a_sig_type,
+                    const void* a_seed,
+                    size_t a_seed_size,
+                    const char *a_pass
+                                        )
 {
-    dap_chain_wallet_t * l_wallet = DAP_NEW_Z(dap_chain_wallet_t);
-    DAP_CHAIN_WALLET_INTERNAL_LOCAL_NEW(l_wallet);
-    l_wallet->name = strdup(a_wallet_name);
-    l_wallet_internal->certs_count = 1;
-    l_wallet_internal->certs = DAP_NEW_Z_SIZE(dap_cert_t *,l_wallet_internal->certs_count * sizeof(dap_cert_t *));
+dap_chain_wallet_t *l_wallet;
+dap_chain_wallet_internal_t *l_wallet_internal;
+int l_rc, l_wallet_name_len, l_pass_len;
+
+    /* Sanity checks ... */
+    if ( DAP_WALLET$SZ_NAME < (l_wallet_name_len = strnlen(a_wallet_name, DAP_WALLET$SZ_NAME + 1)) )
+        return  log_it(L_ERROR, "Wallet's name is too long ( > %d)",  DAP_WALLET$SZ_NAME), NULL;
+
+    if ( DAP_WALLET$SZ_PASS < (l_pass_len = strnlen(a_wallet_name, DAP_WALLET$SZ_PASS + 1)) )
+        return  log_it(L_ERROR, "Wallet's password is too long ( > %d)", DAP_WALLET$SZ_PASS), NULL;
+
+    if ( !(l_wallet = DAP_NEW_Z(dap_chain_wallet_t)) )
+         return log_it(L_ERROR, "Memory allocation error, errno=&d", errno), NULL;
 
-    size_t l_file_name_size = strlen(a_wallet_name)+strlen(a_wallets_path)+13;
-    l_wallet_internal->file_name = DAP_NEW_Z_SIZE (char, l_file_name_size);
+    if ( !(l_wallet->_internal = l_wallet_internal = DAP_NEW_Z(dap_chain_wallet_internal_t)) )
+        return DAP_DELETE(l_wallet), log_it(L_ERROR, "Memory allocation error, errno=&d", errno), NULL;
 
-    dap_snprintf(l_wallet_internal->file_name,l_file_name_size,"%s/%s.dwallet",a_wallets_path,a_wallet_name);
 
-    l_wallet_internal->certs[0] = dap_cert_generate_mem_with_seed(a_wallet_name,
-                                                         dap_sign_type_to_key_type(a_sig_type), a_seed, a_seed_size);
 
+    strncpy(l_wallet->name, a_wallet_name, DAP_WALLET$SZ_NAME);
+    l_wallet_internal->certs_count = 1;
+    l_wallet_internal->certs = DAP_NEW_Z_SIZE(dap_cert_t *,l_wallet_internal->certs_count * sizeof(dap_cert_t *));
+    assert(l_wallet_internal->certs);
+
+    dap_snprintf(l_wallet_internal->file_name, sizeof(l_wallet_internal->file_name)  - 1, "%s/%s%s", a_wallets_path, a_wallet_name, s_wallet_ext);
+
+    l_wallet_internal->certs[0] = dap_cert_generate_mem_with_seed(a_wallet_name, dap_sign_type_to_key_type(a_sig_type), a_seed, a_seed_size);
 
-    if ( dap_chain_wallet_save(l_wallet) == 0 )
+    if ( !(l_rc = dap_chain_wallet_save(l_wallet, a_pass))  )
+    {
+        log_it(L_INFO, "Wallet %s has been created (%s)", a_wallet_name, l_wallet_internal->file_name);
         return l_wallet;
-    else {
-        log_it(L_ERROR,"Can't save the new wallet in disk: \"%s\"",strerror(errno));
-        dap_chain_wallet_close(l_wallet);
-        return NULL;
     }
+
+    log_it(L_ERROR,"Can't save the new wallet (%s) to disk, errno=%d", l_wallet_internal->file_name, errno);
+    dap_chain_wallet_close(l_wallet);
+
+    return NULL;
+
 }
 
 /**
@@ -160,9 +450,14 @@ dap_chain_wallet_t * dap_chain_wallet_create_with_seed(const char * a_wallet_nam
  * @details Creates new wallet
  * @return Wallet, new wallet or NULL if errors
  */
-dap_chain_wallet_t * dap_chain_wallet_create(const char * a_wallet_name, const char * a_wallets_path, dap_sign_type_t a_sig_type)
+dap_chain_wallet_t * dap_chain_wallet_create(
+                const char * a_wallet_name,
+                const char * a_wallets_path,
+                dap_sign_type_t a_sig_type,
+                const char *a_pass
+                                    )
 {
-    return dap_chain_wallet_create_with_seed(a_wallet_name, a_wallets_path, a_sig_type, NULL, 0);
+    return dap_chain_wallet_create_with_seed(a_wallet_name, a_wallets_path, a_sig_type, NULL, 0, a_pass);
 }
 
 /**
@@ -171,24 +466,22 @@ dap_chain_wallet_t * dap_chain_wallet_create(const char * a_wallet_name, const c
  */
 void dap_chain_wallet_close( dap_chain_wallet_t * a_wallet)
 {
+dap_chain_wallet_internal_t * l_wallet_internal;
+
     if(!a_wallet)
         return;
-    DAP_CHAIN_WALLET_INTERNAL_LOCAL(a_wallet);
-    if(a_wallet->name)
-        DAP_DELETE (a_wallet->name);
+
     // TODO Make clean struct dap_chain_wallet_internal_t (certs, addr)
-    if(l_wallet_internal){
-        if(l_wallet_internal->addr)
-            DAP_DELETE(l_wallet_internal->addr);
-        if(l_wallet_internal->file_name)
-            DAP_DELETE(l_wallet_internal->file_name);
+    if ( (l_wallet_internal = a_wallet->_internal) )
+    {
         if ( l_wallet_internal->certs )
-            for(size_t i = 0; i<l_wallet_internal->certs_count;i++)
+            for(size_t i = 0; i < l_wallet_internal->certs_count; i++)
                 dap_cert_delete( l_wallet_internal->certs[i]);
-        DAP_DELETE(l_wallet_internal->certs);
 
+        DAP_DELETE(l_wallet_internal->certs);
         DAP_DELETE(l_wallet_internal);
     }
+
     DAP_DELETE(a_wallet);
 }
 
@@ -202,8 +495,10 @@ dap_chain_addr_t* dap_chain_wallet_get_addr(dap_chain_wallet_t * a_wallet, dap_c
 {
     if(!a_wallet)
         return NULL;
+
     DAP_CHAIN_WALLET_INTERNAL_LOCAL(a_wallet);
-    return a_net_id.uint64? dap_cert_to_addr (l_wallet_internal->certs[0], a_net_id) : NULL;
+
+    return a_net_id.uint64 ? dap_cert_to_addr (l_wallet_internal->certs[0], a_net_id) : NULL;
 }
 
 /**
@@ -228,12 +523,14 @@ dap_chain_addr_t * dap_cert_to_addr(dap_cert_t * a_cert, dap_chain_net_id_t a_ne
 dap_pkey_t* dap_chain_wallet_get_pkey( dap_chain_wallet_t * a_wallet,uint32_t a_pkey_idx )
 {
     DAP_CHAIN_WALLET_INTERNAL_LOCAL(a_wallet);
-    if( l_wallet_internal->certs_count > a_pkey_idx ){
+
+    if( l_wallet_internal->certs_count > a_pkey_idx )
         return dap_cert_to_pkey(l_wallet_internal->certs[a_pkey_idx]);
-    }else{
-        log_it( L_WARNING, "No pkey with index %u in the wallet (total size %zu)",a_pkey_idx,l_wallet_internal->certs_count);
-        return 0;
-    }
+
+
+    log_it( L_WARNING, "No pkey with index %u in the wallet (total size %zu)", a_pkey_idx, l_wallet_internal->certs_count);
+    return 0;
+
 }
 
 /**
@@ -259,161 +556,339 @@ dap_enc_key_t* dap_chain_wallet_get_key( dap_chain_wallet_t * a_wallet,uint32_t
 {
     if(!a_wallet)
         return NULL;
+
     DAP_CHAIN_WALLET_INTERNAL_LOCAL(a_wallet);
-    if( l_wallet_internal->certs_count > a_pkey_idx ){
-        return l_wallet_internal->certs[a_pkey_idx] ?
-                    l_wallet_internal->certs[a_pkey_idx]->enc_key
-                  : NULL;
-    }else{
-        log_it( L_WARNING, "No key with index %u in the wallet (total size %zu)",a_pkey_idx,l_wallet_internal->certs_count);
-        return 0;
-    }
+
+    if( l_wallet_internal->certs_count > a_pkey_idx )
+        return l_wallet_internal->certs[a_pkey_idx] ? l_wallet_internal->certs[a_pkey_idx]->enc_key : NULL;
+
+    log_it( L_WARNING, "No key with index %u in the wallet (total size %zu)",a_pkey_idx,l_wallet_internal->certs_count);
+    return 0;
 }
 
 
-/**
- * @brief dap_chain_wallet_save
- * @param a_wallet
- * @return
+/*
+ *  DESCRIPTION: Save memory wallet's context into the protected by given password.
+ *
+ *  INPUTS:
+ *      a_wallet:   Wallet's context structure
+ *      a_pass:     A password string to be used to protect wallet's content
+ *
+ *  OUTPUTS:
+ *      NONE
+ *
+ *  RETURNS:
+ *      0       -   SUCCESS
+ *      <errno>
  */
-int dap_chain_wallet_save(dap_chain_wallet_t * a_wallet)
+
+int dap_chain_wallet_save(dap_chain_wallet_t * a_wallet, const char *a_pass)
 {
-    if ( a_wallet ){
-        DAP_CHAIN_WALLET_INTERNAL_LOCAL (a_wallet);
-        FILE * l_file = fopen( l_wallet_internal->file_name ,"wb");
-        if ( l_file ){
-            dap_chain_wallet_file_hdr_t l_file_hdr = {0};
-            l_file_hdr.signature = DAP_CHAIN_WALLETS_FILE_SIGNATURE;
-            l_file_hdr.type = 0;
-            l_file_hdr.version = 1;
-            size_t i;
-            // write header
-            fwrite(&l_file_hdr,1,sizeof(l_file_hdr),l_file);
-            // write name
-            uint16_t name_len = (a_wallet->name) ? (uint16_t)strlen(a_wallet->name) : 0;
-            fwrite(&name_len,1,sizeof(uint16_t),l_file);
-            fwrite(a_wallet->name,1,name_len,l_file);
-            // write certs
-            for ( i = 0; i < l_wallet_internal->certs_count ; i ++) {
-                dap_chain_wallet_cert_hdr_t l_wallet_cert_hdr = {0};
-                l_wallet_cert_hdr.version = 1;
-                uint32_t l_cert_raw_size=0;
-                uint8_t * l_buf = dap_cert_mem_save(l_wallet_internal->certs[i], &l_cert_raw_size);
-                l_wallet_cert_hdr.cert_raw_size= l_cert_raw_size;
-                fwrite( &l_wallet_cert_hdr,1, sizeof (l_wallet_cert_hdr), l_file);
-                if ( l_buf ){
-                    fwrite( l_buf, 1, l_wallet_cert_hdr.cert_raw_size, l_file);
-                }else{
-                    log_it(L_WARNING,"Cant write cert to  file %s: error \"%s\"",l_wallet_internal->file_name,
-                           strerror(errno));
-                }
-                DAP_DELETE (l_buf);
-            }
-            fclose (l_file);
-            return 0;
-        }else{
-            log_it(L_ERROR,"Cant open file %s for writting",l_wallet_internal->file_name);
-            return -2;
+DAP_CHAIN_WALLET_INTERNAL_LOCAL (a_wallet);                                 /* Declare l_wallet_internal */
+int l_fd = -1, l_rc = 0, l_len = 0;
+dap_chain_wallet_file_hdr_t l_file_hdr = {0};
+dap_chain_wallet_cert_hdr_t l_wallet_cert_hdr = {0};
+char *l_cp, *l_cert_raw, l_buf[32*1024];
+dap_enc_key_t *l_enc_key = NULL;
+uint32_t    csum = CRC32C_INIT;
+enum    { WALLET$K_IOV_HEADER = 0, WALLET$K_IOV_BODY, WALLET$SZ_IOV_NR};
+struct iovec l_iov [ WALLET$SZ_IOV_NR ];
+
+    if ( !a_wallet )
+        return  log_it(L_ERROR, "Wallet is null, can't save it to file!"), -EINVAL;
+
+    if ( a_pass )
+        if ( !(l_enc_key = dap_enc_key_new_generate(DAP_ENC_KEY_TYPE_GOST_OFB, NULL, 0, a_pass, strlen(a_pass), 0)) )
+            return  log_it(L_ERROR, "Error create key context"), -EINVAL;
+
+    if ( 0 > (l_fd = open(l_wallet_internal->file_name , O_CREAT | O_WRONLY, s_fileprot)) )
+        return  log_it(L_ERROR,"Cant open file %s for writting, errno=%d", l_wallet_internal->file_name, errno), -errno;
+
+    l_file_hdr.signature = DAP_CHAIN_WALLETS_FILE_SIGNATURE;                /* Fill and write Wallet's file header */
+    l_file_hdr.type = a_pass ? DAP_WALLET$K_TYPE_GOST89 : DAP_WALLET$K_TYPE_PLAIN;
+    l_file_hdr.version = a_pass ? DAP_WALLET$K_VER_2 : DAP_WALLET$K_VER_1;
+
+    l_cp  = a_wallet->name ? a_wallet->name : "Bad-MotherFuqqer-Wallet";    /* What ?! */
+    l_file_hdr.wallet_len = strnlen(l_cp, DAP_WALLET$SZ_NAME);
+    l_file_hdr.wallet_len += 1;                                             /* Special ASCIZ for advanced programmers */
+
+    l_iov[WALLET$K_IOV_HEADER].iov_base = &l_file_hdr;
+    l_len = l_iov[WALLET$K_IOV_HEADER].iov_len = sizeof(l_file_hdr);
+
+    l_iov[WALLET$K_IOV_BODY].iov_base = l_cp;
+    l_len += l_iov[WALLET$K_IOV_BODY].iov_len = l_file_hdr.wallet_len;
+
+    l_rc = writev (l_fd, l_iov, WALLET$SZ_IOV_NR );                         /* Performs writting vectorized buffer */
+    if ( l_len != l_rc )
+    {
+        close(l_fd);
+        return  log_it(L_ERROR, "Error write Wallet header to file '%s', errno=%d", l_wallet_internal->file_name, errno), -EIO;
+    }
+
+                                                                            /* CRC for file header part */
+    csum = crc32c (csum, l_iov[WALLET$K_IOV_HEADER].iov_base, l_iov[WALLET$K_IOV_HEADER].iov_len);
+                                                                            /* CRC for file body part */
+    csum = crc32c (csum, l_iov[WALLET$K_IOV_BODY].iov_base, l_iov[WALLET$K_IOV_BODY].iov_len);
+
+    /* Write certs */
+    for ( size_t i = 0; i < l_wallet_internal->certs_count ; i++)
+    {
+                                                                            /* Get ceritificate body */
+        if ( !(l_cert_raw  = (char *) dap_cert_mem_save(l_wallet_internal->certs[i], (uint32_t *) &l_len)) )
+        {
+            log_it(L_WARNING, "Certificate #%zu cannot be obtained, go next ...", i);
+            continue;
+        }
+
+        csum = crc32c (csum, l_cert_raw, l_len);                          /* CRC for every certificate */
+
+        if ( l_enc_key )
+        {
+            /* Encrypt buffer with cert to local storage,
+             * be advised that we don't performs a source buffer aligment preparation according to
+             * block nature of the GOST family and other block-cyphers. We expect that this work is performed
+             * by the "enc_na" internaly. So , relax mothefackerzzz!
+             */
+            l_len = l_enc_key->enc_na(l_enc_key, l_cert_raw, l_len, l_buf, sizeof(l_buf) );
         }
-    }else{
-        log_it(L_ERROR,"Wallet is null, can't save it to file!");
-        return -1;
+
+        l_wallet_cert_hdr.type = DAP_WALLET$K_CERT;                         /* Prepare on-disk cert record header */
+        l_wallet_cert_hdr.cert_raw_size = l_len;
+
+        /*
+         * Gather chunks for I/O
+        */
+        l_len = 0;                                                          /* Total octets to be writtent to disk */
+
+        l_iov[WALLET$K_IOV_HEADER].iov_base  = &l_wallet_cert_hdr;          /* Cert's record header */
+        l_len += l_iov[WALLET$K_IOV_HEADER].iov_len  = sizeof(l_wallet_cert_hdr);
+
+        l_iov[WALLET$K_IOV_BODY].iov_base  = l_enc_key ? l_buf : l_cert_raw;/* Cert itself or buffer with has been encrypted cert */
+        l_len += l_iov[WALLET$K_IOV_BODY].iov_len  = l_wallet_cert_hdr.cert_raw_size;
+
+        l_rc = writev (l_fd, l_iov, WALLET$SZ_IOV_NR );                      /* Perform writting vectorized buffer */
+        DAP_DEL_Z (l_cert_raw);                                             /* Free cert's memory */
+        if ( l_rc != l_len )                                                /* Check a result of the I/O operation */
+        {
+            close (l_fd);
+            return  log_it(L_ERROR, "Error write %d octets of cert to  file '%s', errno=%d", l_len, l_wallet_internal->file_name, errno), errno;
+        }
+    }
+
+    if ( l_file_hdr.version == DAP_WALLET$K_VER_2 )
+    {
+        l_wallet_cert_hdr.type = DAP_WALLET$K_MAGIC;
+        l_wallet_cert_hdr.cert_raw_size = sizeof(csum);
+
+        l_len = 0;                                                          /* Total octets to be writtent to disk */
+
+        l_iov[WALLET$K_IOV_HEADER].iov_base  = &l_wallet_cert_hdr;
+        l_len += l_iov[WALLET$K_IOV_HEADER].iov_len  = sizeof(l_wallet_cert_hdr);
+
+        l_iov[WALLET$K_IOV_BODY].iov_base  = &csum;
+        l_len += l_iov[WALLET$K_IOV_BODY].iov_len  = sizeof(csum);
+
+        l_rc = writev (l_fd, l_iov, WALLET$SZ_IOV_NR );                     /* Perform writting vectorized buffer */
+        if ( l_rc != l_len )                                                /* Check a result of the I/O operation */
+            log_it(L_ERROR, "Error write %d octets of cert to  file '%s', errno=%d", l_len, l_wallet_internal->file_name, errno);
     }
+
+    /* Cleanup and exit ... */
+    close (l_fd);
+
+    if ( l_enc_key )
+        dap_enc_key_delete(l_enc_key);
+
+
+
+#ifdef  DAP_SYS_DEBUG                                                       /* @RRL: For debug purpose only!!! */
+    {
+    dap_chain_wallet_t  *l_wallet;
+
+    if ( (l_wallet = dap_chain_wallet_open_file (l_wallet_internal->file_name, a_pass)) )
+        dap_chain_wallet_close(l_wallet);
+
+    }
+#endif      /* DAP_SYS_DEBUG */
+
+    return  log_it(L_NOTICE, "Wallet '%s' has been saved into the '%s'", a_wallet->name, l_wallet_internal->file_name), 0;
 }
 
+
+
 /**
  * @brief dap_chain_wallet_open_file
  * @param a_file_name
  * @return
  */
-dap_chain_wallet_t * dap_chain_wallet_open_file(const char * a_file_name)
+dap_chain_wallet_t *dap_chain_wallet_open_file (
+                    const char *a_file_name,
+                    const char *l_pass
+                    )
 {
-    FILE * l_file = fopen( a_file_name ,"rb");
-    if(!l_file){
-        log_it(L_WARNING,"Can't open wallet file %s",a_file_name);
-        return NULL;
+dap_chain_wallet_t *l_wallet;
+int l_fd = -1, l_rc, l_certs_count, l_len;
+dap_chain_wallet_file_hdr_t l_file_hdr = {0};
+dap_chain_wallet_cert_hdr_t l_cert_hdr = {0};
+char l_buf[32*1024], l_buf2[32*1024], *l_bufp, l_wallet_name [DAP_WALLET$SZ_NAME] = {0};
+dap_enc_key_t *l_enc_key = NULL;
+uint32_t    l_csum = CRC32C_INIT, l_csum2 = CRC32C_INIT;
+
+    if ( 0 > (l_fd = open(a_file_name , O_RDONLY)) )                        /* Open file for ReadOnly !!! */
+        return  log_it(L_ERROR,"Cant open file %s for read, errno=%d", a_file_name, errno), NULL;
+
+    if ( sizeof(l_file_hdr) != read(l_fd, &l_file_hdr, sizeof(l_file_hdr)) )/* Get the file header record */
+        return  log_it(L_ERROR, "Error read Wallet file (%s) header, errno=%d", a_file_name, errno), close(l_fd), NULL;
+
+    if ( l_file_hdr.signature != DAP_CHAIN_WALLETS_FILE_SIGNATURE )         /* Check signature of the file */
+        return  log_it(L_ERROR, "Wallet (%s) signature mismatch (%#lx != %#lx", a_file_name, l_file_hdr.signature, DAP_CHAIN_WALLETS_FILE_SIGNATURE),
+                    close(l_fd), NULL;
+
+    if ( (l_file_hdr.version == DAP_WALLET$K_VER_2) && (!l_pass) )
+        return  log_it(L_DEBUG, "Wallet (%s) version 2 cannot be processed w/o password", a_file_name), close(l_fd), NULL;
+
+    if ( l_file_hdr.wallet_len > DAP_WALLET$SZ_NAME )
+        return  log_it(L_ERROR, "Invalid Wallet name (%s) length ( >%d)", a_file_name, DAP_WALLET$SZ_NAME),
+                    close(l_fd), NULL;
+
+    if ( l_file_hdr.wallet_len != read(l_fd, l_wallet_name, l_file_hdr.wallet_len) ) /* Read wallet's name */
+        return  log_it(L_ERROR, "Error Wallet's name (%s), errno=%d", a_file_name, errno), close(l_fd), NULL;
+
+
+    l_csum = crc32c (l_csum, &l_file_hdr, sizeof(l_file_hdr) );           /* Compute check sum of the Wallet file header */
+    l_csum = crc32c (l_csum, l_wallet_name,  l_file_hdr.wallet_len);
+
+    log_it(L_DEBUG, "Wallet file: %s, Wallet[Version: %d, type: %d, name: '%.*s']",
+           a_file_name, l_file_hdr.version, l_file_hdr.type, l_file_hdr.wallet_len, l_wallet_name);
+
+    /* First run - count certs in file */
+    for ( l_certs_count = 0; sizeof(l_cert_hdr) == (l_rc = read (l_fd, &l_cert_hdr, sizeof(l_cert_hdr))); l_certs_count++ ) {
+        if ( (l_file_hdr.version == DAP_WALLET$K_VER_2) && (l_cert_hdr.type == DAP_WALLET$K_MAGIC) )
+            break;
+
+        if ( (int) l_cert_hdr.cert_raw_size != (l_rc = read(l_fd, l_buf, l_cert_hdr.cert_raw_size)) ) {
+            log_it(L_ERROR, "Error read certificate's body (%d != %d), errno=%d", l_cert_hdr.cert_raw_size, l_rc, errno);
+            break;
+        }
     }
-    fseek(l_file, 0L, SEEK_END);
-    uint64_t l_file_size = ftell(l_file);
-    rewind(l_file);
-
-    if ( l_file ){
-        dap_chain_wallet_file_hdr_t l_file_hdr={0};
-        // read header
-        if ( fread(&l_file_hdr,1,sizeof(l_file_hdr),l_file) == sizeof (l_file_hdr) ) {
-            if ( l_file_hdr.signature == DAP_CHAIN_WALLETS_FILE_SIGNATURE ) {
-                dap_chain_wallet_t * l_wallet = DAP_NEW_Z(dap_chain_wallet_t);
-                DAP_CHAIN_WALLET_INTERNAL_LOCAL_NEW(l_wallet);
-                // read name
-                uint16_t name_len = 0;
-                fread(&name_len, 1, sizeof(uint16_t), l_file);
-                l_wallet->name = DAP_NEW_Z_SIZE(char, name_len + 1);
-                fread(l_wallet->name, 1, name_len, l_file);
-
-                l_wallet_internal->file_name = strdup(a_file_name);
-                size_t i = sizeof (l_file_hdr) + sizeof(uint16_t) + name_len;
-                // calculate certs count
-                while (i <  l_file_size ){
-                    dap_chain_wallet_cert_hdr_t l_cert_hdr={0};
-                    fread(&l_cert_hdr,1,sizeof(l_cert_hdr),l_file);
-                    i+=sizeof(l_cert_hdr);
-                    if (l_cert_hdr.cert_raw_size > 0 ){
-                        if(l_cert_hdr.cert_raw_size <= (l_file_size - i)) {
-                            i+=l_cert_hdr.cert_raw_size;
-                            l_wallet_internal->certs_count++;
-                        }else{
-                            log_it(L_WARNING,"Wrong raw cert size %u (too big)",l_cert_hdr.cert_raw_size);
-                            break;
-                        }
-                    }else{
-                        log_it(L_WARNING,"Wrong raw cert size 0");
-                        break;
-                    }
-                }
-                if(l_wallet_internal->certs_count){
-                    // read certs
-                    fseek(l_file,sizeof (l_file_hdr) + sizeof(uint16_t) + name_len,SEEK_SET);
-                    l_wallet_internal->certs = DAP_NEW_Z_SIZE(dap_cert_t *,l_wallet_internal->certs_count * sizeof(dap_cert_t *));
-                    for (i = 0; i < l_wallet_internal->certs_count; i++ ){
-                        dap_chain_wallet_cert_hdr_t l_cert_hdr={0};
-                        fread(&l_cert_hdr,1,sizeof(l_cert_hdr),l_file);
-                        uint8_t * l_data = DAP_NEW_SIZE(uint8_t,l_cert_hdr.cert_raw_size);
-                        fread(l_data,1,l_cert_hdr.cert_raw_size,l_file);
-                        l_wallet_internal->certs[i] = dap_cert_mem_load(l_data,l_cert_hdr.cert_raw_size);
-                        DAP_DELETE (l_data);
-                    }
-                }else
-                    log_it(L_WARNING,"Corrupted wallet file, no certs found in it");
-                fclose(l_file);
-                return l_wallet;
-            } else {
-                log_it(L_ERROR,"Wrong wallet file signature: corrupted file or wrong format");
-                return NULL;
-            }
-        }else{
-            log_it(L_ERROR,"Can't read wallet's header %s: \"%s\"",a_file_name,strerror(errno));
-            return NULL;
+
+    if ( l_rc < 0 )
+        return  log_it(L_ERROR, "Wallet file (%s) I/O error, errno=%d", a_file_name, errno), close(l_fd), NULL;
+
+    if ( !l_certs_count )
+        return  log_it(L_ERROR, "No certificate (-s) in the wallet file (%s)", a_file_name), close(l_fd), NULL;
+
+
+    if ( (l_file_hdr.version == DAP_WALLET$K_VER_2) && l_pass )             /* Generate encryptor context  */
+        if ( !(l_enc_key = dap_enc_key_new_generate(DAP_ENC_KEY_TYPE_GOST_OFB, NULL, 0, l_pass, strlen(l_pass), 0)) )
+            return  log_it(L_ERROR, "Error create key context"), close(l_fd), NULL;
+
+
+    /* Create local instance of wallet,
+     * allocate memory for array to keep loaded certs */
+    l_wallet = DAP_NEW_Z(dap_chain_wallet_t);
+    assert(l_wallet);
+    DAP_CHAIN_WALLET_INTERNAL_LOCAL_NEW(l_wallet);
+    assert(l_wallet_internal);
+
+    dap_snprintf(l_wallet->name, DAP_WALLET$SZ_NAME, "%.*s", l_file_hdr.wallet_len, l_wallet_name);
+    strncpy(l_wallet_internal->file_name, a_file_name, sizeof(l_wallet_internal->file_name) );
+
+    l_wallet_internal->certs_count = l_certs_count;
+    assert(l_wallet_internal->certs_count);
+
+    l_wallet_internal->certs = DAP_NEW_Z_SIZE(dap_cert_t *, l_wallet_internal->certs_count * sizeof(dap_cert_t *));
+    assert(l_wallet_internal->certs);
+
+
+
+    lseek(l_fd,  sizeof(l_file_hdr) + l_file_hdr.wallet_len, SEEK_SET);     /* Set file pointer to first record after cert file header */
+
+
+
+    for ( size_t i = 0; sizeof(l_cert_hdr) == (l_rc = read (l_fd, &l_cert_hdr, sizeof(l_cert_hdr))); i++ )           /* Read Cert/Record header */
+    {
+        if ( (int) l_cert_hdr.cert_raw_size != (l_rc = read(l_fd, l_buf, l_cert_hdr.cert_raw_size)) ) {
+            log_it(L_ERROR, "Error read certificate's body (%d != %d), errno=%d", l_cert_hdr.cert_raw_size, l_rc, errno);
+            break;
         }
-    }else{
-        log_it(L_ERROR,"Can't open file %s: \"%s\"",a_file_name,strerror(errno));
-        return NULL;
+
+        if ( (l_file_hdr.version == DAP_WALLET$K_VER_2) && (l_cert_hdr.type == DAP_WALLET$K_MAGIC) ) {
+            l_csum2 = *((uint32_t *) &l_buf);                               /* CRC32 must be terminal element in the wallet file */
+            break;
+        }
+
+
+        l_bufp = l_buf;
+
+        if ( l_enc_key )
+        {
+            l_len = l_enc_key->dec_na(l_enc_key, l_buf, l_rc, l_buf2, sizeof(l_buf2) );
+            l_bufp = l_buf2;
+            l_csum = crc32c (l_csum, l_bufp, l_len);                          /* CRC for every certificate */
+        }
+
+        l_wallet_internal->certs[ i ] = dap_cert_mem_load(l_bufp, l_cert_hdr.cert_raw_size);
+    }
+
+
+
+    /* Cleanup and exit ... */
+    close (l_fd);
+
+    if ( l_enc_key )
+    {
+        l_wallet->flags |= (DAP_WALLET$M_FL_PROTECTED | DAP_WALLET$M_FL_ACTIVE);
+        if ( l_csum != l_csum2 )
+        {
+            log_it(L_ERROR, "Wallet checksum mismatch, %#x <> %#x", l_csum, l_csum2);
+            dap_chain_wallet_close( l_wallet);
+            l_wallet = NULL;
+        }
+
+        dap_enc_key_delete(l_enc_key);
     }
+
+    return  l_wallet;
 }
 
+
+
+
+
 /**
  * @brief dap_chain_wallet_open
  * @param a_wallet_name
  * @param a_wallets_path
  * @return
  */
-dap_chain_wallet_t * dap_chain_wallet_open(const char * a_wallet_name, const char * a_wallets_path)
+dap_chain_wallet_t *dap_chain_wallet_open (
+                        const char *a_wallet_name,
+                        const char *a_wallets_path
+                                    )
 {
+char l_file_name [MAX_PATH] = {0}, l_pass [ DAP_WALLET$SZ_PASS + 3] = {0},
+        *l_cp, l_wallet_name[DAP_WALLET$SZ_PASS + 3] = {0};
+ssize_t     l_rc, l_pass_len;
+
+    /* Sanity checks */
     if(!a_wallet_name || !a_wallets_path)
         return NULL;
-    char *l_file_name = dap_strdup_printf("%s/%s.dwallet", a_wallets_path, a_wallet_name);
-    dap_chain_wallet_t * l_wallet = dap_chain_wallet_open_file(l_file_name);
-    DAP_DELETE(l_file_name);
-    return l_wallet;
+
+    if ( (l_cp = strstr(a_wallet_name, s_wallet_ext)) )
+        strncpy(l_wallet_name, a_wallet_name, l_cp - a_wallet_name);
+    else strcpy(l_wallet_name, a_wallet_name);
+
+    dap_snprintf(l_file_name, sizeof(l_file_name) - 1, "%s/%s%s", a_wallets_path, l_wallet_name, s_wallet_ext);
+
+
+    l_pass_len = DAP_WALLET$SZ_PASS;                                    /* Size of the buffer for password */
+                                                                        /* Lookup password in the internal hash-table */
+    if ( (l_rc = s_dap_chain_wallet_pass (l_wallet_name, strlen(l_wallet_name), l_pass, &l_pass_len)) )
+        l_pass_len = 0;
+
+
+    return  dap_chain_wallet_open_file(l_file_name, l_pass_len ? l_pass : NULL);
 }
 
 /**
@@ -422,15 +897,14 @@ dap_chain_wallet_t * dap_chain_wallet_open(const char * a_wallet_name, const cha
  * @param a_net_id
  * @return
  */
-uint256_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, const char *a_token_ticker)
+uint256_t dap_chain_wallet_get_balance (
+            dap_chain_wallet_t *a_wallet,
+            dap_chain_net_id_t a_net_id,
+            const char *a_token_ticker
+                                    )
 {
     dap_chain_net_t *l_net = dap_chain_net_by_id(a_net_id);
-    dap_chain_addr_t *l_addr =dap_chain_wallet_get_addr(a_wallet, a_net_id);
-    uint256_t l_balance = {};
-    if (l_net)
-    {
-        dap_ledger_t *l_ledger = l_net->pub.ledger;
-        l_balance = dap_chain_ledger_calc_balance(l_ledger, l_addr, a_token_ticker);
-    }
-    return l_balance;
+    dap_chain_addr_t *l_addr = dap_chain_wallet_get_addr(a_wallet, a_net_id);
+
+    return  (l_net)  ? dap_chain_ledger_calc_balance(l_net->pub.ledger, l_addr, a_token_ticker) : uint256_0;
 }
diff --git a/modules/wallet/include/dap_chain_wallet.h b/modules/wallet/include/dap_chain_wallet.h
index 51ffd4c91ba18f5af2488f0d9855736e30211c31..8c7448389afae88207910ec8b74e58c868ee9da0 100644
--- a/modules/wallet/include/dap_chain_wallet.h
+++ b/modules/wallet/include/dap_chain_wallet.h
@@ -30,10 +30,19 @@
 #include "dap_sign.h"
 #include "dap_cert.h"
 
+
+/* @RRL: #6131 */
+#define DAP_WALLET$SZ_NAME  64                                              /* Maximum length of the wallet's name */
+#define DAP_WALLET$SZ_PASS  64                                              /* Maximum length of the wallet's password */
+
+#define DAP_WALLET$M_FL_PROTECTED        (1 << 0)                           /* Wallet is password protected */
+#define DAP_WALLET$M_FL_ACTIVE           (1 << 1)                           /* Has been activated (has been open with password) */
+
 typedef struct dap_chain_wallet{
-    char * name;
-    void * _internal;
-    void * _inheritor;
+    char        name[ DAP_WALLET$SZ_NAME + 1 ];                             /* Human readable name of BMF Wallet */
+    uint64_t    flags;                                                      /* See DAP_WALLET$M_FL_* constants */
+    void        *_internal;
+    void        *_inheritor;
 } dap_chain_wallet_t;
 
 
@@ -42,12 +51,19 @@ void dap_chain_wallet_deinit(void);
 
 const char* dap_chain_wallet_get_path(dap_config_t * a_config);
 
+/* @RRL: #6131 - Password protected BMF Wallet */
 dap_chain_wallet_t * dap_chain_wallet_create_with_seed(const char * a_wallet_name, const char * a_wallets_path,
-        dap_sign_type_t a_sig_type, const void* a_seed, size_t a_seed_size);
-dap_chain_wallet_t * dap_chain_wallet_create(const char * a_wallet_name, const char * a_wallets_path, dap_sign_type_t a_sig_type); // Creates new one if not found
-dap_chain_wallet_t * dap_chain_wallet_open_file(const char * a_file_name);
-dap_chain_wallet_t * dap_chain_wallet_open(const char * a_wallet_name, const char * a_wallets_path);
-int dap_chain_wallet_save(dap_chain_wallet_t * a_wallet);
+        dap_sign_type_t a_sig_type, const void* a_seed, size_t a_seed_size, const char *a_pass);
+
+dap_chain_wallet_t * dap_chain_wallet_create_with_pass(const char * a_wallet_name, const char * a_wallets_path,
+        const void* a_pass, size_t a_pass_sz);
+
+
+dap_chain_wallet_t  *dap_chain_wallet_create(const char * a_wallet_name, const char * a_wallets_path, dap_sign_type_t a_sig_type, const char *a_pass); // Creates new one if not found
+dap_chain_wallet_t  *dap_chain_wallet_open_file(const char * a_file_name, const char *a_pass);
+dap_chain_wallet_t *dap_chain_wallet_open(const char * a_wallet_name, const char * a_wallets_path);
+dap_chain_wallet_t *dap_chain_wallet_open_ext(const char * a_wallet_name, const char * a_wallets_path, const char *pass);
+int dap_chain_wallet_save(dap_chain_wallet_t * a_wallet, const char *a_pass);
 
 void dap_chain_wallet_close( dap_chain_wallet_t * a_wallet);
 
@@ -61,3 +77,6 @@ dap_enc_key_t * dap_chain_wallet_get_key( dap_chain_wallet_t * a_wallet,uint32_t
 uint256_t dap_chain_wallet_get_balance(dap_chain_wallet_t *a_wallet, dap_chain_net_id_t a_net_id, const char *a_token_ticker);
 
 int dap_chain_wallet_save_file( dap_chain_wallet_t * a_wallet);
+
+int     dap_chain_wallet_activate   (const char *a_name, ssize_t a_name_len, const char *a_pass, ssize_t a_pass_len, unsigned a_ttl);
+int     dap_chain_wallet_deactivate   (const char *a_name, ssize_t a_name_len, const char *a_pass, ssize_t a_pass_len);
diff --git a/modules/wallet/include/dap_chain_wallet_internal.h b/modules/wallet/include/dap_chain_wallet_internal.h
index 0256080f4ce8e719a929b31a81302e3d6d4b47ff..ef746aede875d65eab533869ea450c6ee6273ee8 100644
--- a/modules/wallet/include/dap_chain_wallet_internal.h
+++ b/modules/wallet/include/dap_chain_wallet_internal.h
@@ -27,13 +27,42 @@
 #include "dap_cert.h"
 #include "dap_cert_file.h"
 #include "dap_chain_common.h"
-
 #include "dap_chain_wallet.h"
 
 #define DAP_CHAIN_WALLETS_FILE_SIGNATURE 0x1a167bef15feea18
 
+
+enum    {
+    DAP_WALLET$K_TYPE_PLAIN = 0,                                            /* 0x00 - uncompressed and unencrypted */
+    DAP_WALLET$K_TYPE_GOST89 = 1,                                           /* Encrypted with the GOST 89 */
+};
+
+enum    {
+    DAP_WALLET$K_VER_1 = 1,                                                 /* Wallet's file structure version, entry level */
+    DAP_WALLET$K_VER_2 = 2,                                                 /* BMF Level */
+};
+
+
+enum    {
+    DAP_WALLET$K_CERT = 1,                                                  /* Cert record type */
+    DAP_WALLET$K_MAGIC = 2,                                                 /* Record is magic sequence */
+};
+
+
+typedef struct dap_chain_wallet_n_pass {
+    uint16_t    name_len;                                                   /* Length of the follows wallet's name string */
+    char        name[DAP_WALLET$SZ_NAME + 1];
+    uint16_t    pass_len;                                                   /* Length of the follows wallet's password string */
+    char        pass[DAP_WALLET$SZ_PASS + 1];
+
+    struct timespec exptm;                                                  /* A time of expiration of the record
+                                                                              need RE-Activation steps */
+
+    UT_hash_handle hh;                                                      /* Context for hash-table */
+} dap_chain_wallet_n_pass_t;
+
 typedef struct dap_chain_wallet_cert_hdr{
-    uint32_t version;
+    uint32_t type;                                                          /* See DAP_WALLET$K_CERT/MAGIC ...constants */
     uint32_t cert_raw_size; /// Certificate size
 } DAP_ALIGN_PACKED dap_chain_wallet_cert_hdr_t;
 
@@ -43,13 +72,15 @@ typedef struct dap_chain_wallet_cert{
 } DAP_ALIGN_PACKED dap_chain_wallet_cert_t;
 
 typedef struct dap_chain_wallet_file_hdr{
-    uint64_t signature;
-    uint32_t version;
-    uint8_t type; /// Wallets storage type 0x00 - uncompressed and unencrypted
-    uint64_t padding;
+    uint64_t    signature;
+    uint32_t    version;
+    uint8_t     type;                                                       /* See DAP_WALLET$K_TYPE_* constants */
+    uint64_t    padding;
+    uint16_t    wallet_len;                                                 /* Length of the follows wallet's name string */
+    char        wallet_name[];
 } DAP_ALIGN_PACKED dap_chain_wallet_file_hdr_t;
 
-typedef struct dap_chain_wallet_file
+typedef struct dap_chain_wallet_file                                        /* On-disk structure */
 {
     dap_chain_wallet_file_hdr_t header;
     uint8_t data[];
@@ -57,14 +88,12 @@ typedef struct dap_chain_wallet_file
 
 typedef struct dap_chain_wallet_internal
 {
-    dap_chain_addr_t *addr;
-    char * file_name;
-    size_t certs_count;
-    dap_cert_t ** certs;
+                char    file_name[MAX_PATH];
+                size_t  certs_count;
+            dap_cert_t  **certs;
 } dap_chain_wallet_internal_t;
 
 #define DAP_CHAIN_WALLET_INTERNAL(a) (a ? (dap_chain_wallet_internal_t *) a->_internal : NULL)
-
 #define DAP_CHAIN_WALLET_INTERNAL_LOCAL(a) dap_chain_wallet_internal_t * l_wallet_internal = DAP_CHAIN_WALLET_INTERNAL(a)
-
 #define DAP_CHAIN_WALLET_INTERNAL_LOCAL_NEW(a) dap_chain_wallet_internal_t * l_wallet_internal = DAP_NEW_Z(dap_chain_wallet_internal_t); a->_internal = l_wallet_internal
+