From aa78144ee96348c2c5a683a7077665214ac5cccc Mon Sep 17 00:00:00 2001
From: "alexey.stratulat" <alexey.stratulat@demlabs.net>
Date: Mon, 5 Jun 2023 08:58:30 +0000
Subject: [PATCH] Support 8236

---
 dap-sdk                                       |   2 +-
 modules/chain/dap_chain_ledger.c              | 308 ++++++++++++++----
 modules/chain/include/dap_chain_ledger.h      |   2 +-
 modules/common/dap_chain_datum.c              |  32 +-
 .../common/include/dap_chain_datum_token.h    |   4 +-
 modules/net/dap_chain_node_cli.c              |   6 +
 modules/net/dap_chain_node_cli_cmd.c          |  50 ++-
 modules/net/include/dap_chain_node_cli_cmd.h  |   5 +
 8 files changed, 327 insertions(+), 82 deletions(-)

diff --git a/dap-sdk b/dap-sdk
index 52084ccdc2..e96283ddc2 160000
--- a/dap-sdk
+++ b/dap-sdk
@@ -1 +1 @@
-Subproject commit 52084ccdc2e6d219625937edc77ee56ff1c58f86
+Subproject commit e96283ddc24d41a307badd6365d82cf524685f79
diff --git a/modules/chain/dap_chain_ledger.c b/modules/chain/dap_chain_ledger.c
index a7e66e3450..911f7d94e0 100644
--- a/modules/chain/dap_chain_ledger.c
+++ b/modules/chain/dap_chain_ledger.c
@@ -114,8 +114,8 @@ typedef struct dap_chain_ledger_token_item {
     time_t last_update_token_time;
 
     // for auth operations
-    dap_sign_t ** auth_signs;
-    dap_chain_hash_fast_t * auth_signs_pkey_hash;
+    dap_pkey_t ** auth_pkeys;
+    dap_chain_hash_fast_t *auth_pkeys_hash;
     size_t auth_signs_total;
     size_t auth_signs_valid;
     uint16_t           flags;
@@ -269,6 +269,7 @@ static void s_threshold_txs_proc( dap_ledger_t * a_ledger);
 static void s_threshold_txs_free(dap_ledger_t *a_ledger);
 static void s_threshold_emission_free(dap_ledger_t *a_ledger);
 static int s_token_tsd_parse(dap_ledger_t * a_ledger, dap_chain_ledger_token_item_t *a_token_item , dap_chain_datum_token_t * a_token, size_t a_token_size);
+static int s_tsd_sign_apply(dap_ledger_t *a_ledger, dap_chain_ledger_token_item_t *a_token_item , dap_chain_datum_token_t *a_token, size_t a_token_size);
 static int s_ledger_permissions_check(dap_chain_ledger_token_item_t *  a_token_item, uint16_t a_permission_id, const void * a_data,size_t a_data_size );
 static bool s_ledger_tps_callback(void *a_arg);
 static int s_sort_ledger_tx_item(dap_chain_ledger_tx_item_t* a, dap_chain_ledger_tx_item_t* b);
@@ -437,8 +438,7 @@ static bool s_ledger_token_update_check(dap_chain_ledger_token_item_t *a_cur_tok
 
     l_signs_upd_token = dap_chain_datum_token_signs_parse(a_token_update, a_token_update_size,
                                                           &auth_signs_total, &auth_signs_valid);
-    if (a_cur_token_item->auth_signs_total != auth_signs_total
-    ||	a_cur_token_item->auth_signs_valid != auth_signs_valid) {
+    if (a_cur_token_item->auth_signs_valid > auth_signs_total) {
         DAP_DEL_Z(l_signs_upd_token);
         if(s_debug_more)
             log_it(L_WARNING,"Can't update token with ticker '%s' because: "
@@ -450,13 +450,24 @@ static bool s_ledger_token_update_check(dap_chain_ledger_token_item_t *a_cur_tok
         return false;
     }
     if(auth_signs_total) {
+        size_t l_valid_pkeys = 0;
         for(uint16_t i = 0; i < auth_signs_total; i++){
-            if (!dap_sign_match_pkey_signs(a_cur_token_item->auth_signs[i], l_signs_upd_token[i])) {
-                DAP_DEL_Z(l_signs_upd_token);
-                if(s_debug_more)
-                    log_it(L_WARNING, "Can't update token with ticker '%s' because: Signs not compare", a_token_update->ticker);
-                return false;
+            dap_pkey_t *l_pkey_upd_token = dap_pkey_get_from_sign_deserialization(l_signs_upd_token[i]);
+            for (size_t j = 0; j < a_cur_token_item->auth_signs_total; j++) {
+                if (dap_pkey_match(a_cur_token_item->auth_pkeys[j], l_pkey_upd_token)) {
+                    l_valid_pkeys++;
+                    break;
+                }
             }
+            DAP_DELETE(l_pkey_upd_token);
+        }
+        if (a_cur_token_item->auth_signs_valid > l_valid_pkeys) {
+            DAP_DEL_Z(l_signs_upd_token);
+            if (s_debug_more)
+                log_it(L_WARNING, "Can't update token with ticker '%s' because: Insufficient number of valid signatures "
+                                  "for an token update. Verified %zu needs %zu.", a_token_update->ticker, l_valid_pkeys,
+                                  a_cur_token_item->auth_signs_valid);
+            return false;
         }
     }
     DAP_DEL_Z(l_signs_upd_token);
@@ -467,7 +478,149 @@ static bool s_ledger_token_update_check(dap_chain_ledger_token_item_t *a_cur_tok
             return false;
         }
     }
-    return true;
+    // Check edit auth signs
+    size_t l_tsd_total_size = 0;
+    if (a_token_update->subtype  == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE)
+        l_tsd_total_size = a_token_update->header_native_update.tsd_total_size;
+    else if (a_token_update->subtype  == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE)
+        l_tsd_total_size = a_token_update->header_native_update.tsd_total_size;
+    // Checking that the TSD section with the threshold change is the only one.
+    //And getting lists of TSD sections with the removal and addition of certificates.
+    int l_quantity_tsd_section_edit_signs_emission = 0;
+    dap_tsd_t *l_tsd_signs_valid = NULL;
+    dap_list_t *l_tsd_list_remote_pkeys = NULL;
+    int l_quantity_tsd_remote_pkeys = 0;
+    dap_list_t *l_tsd_list_added_pkeys = NULL;
+    int l_quantity_tsd_add_pkeys = 0;
+    for (size_t l_tsd_offset = 0; l_tsd_offset < l_tsd_total_size; ) {
+        dap_tsd_t *l_tsd = (dap_tsd_t*)((byte_t*)a_token_update->data_n_tsd + l_tsd_offset);
+        size_t l_tsd_size = dap_tsd_size(l_tsd);
+        if (l_tsd_size == 0) {
+            if (s_debug_more)
+                log_it(L_ERROR, "Token refresh datum %s contains a non-valid TSD section. Size TSD section is 0.", a_token_update->ticker);
+            return false;
+        } else if (l_tsd_size + l_tsd_offset > l_tsd_total_size) {
+            if (s_debug_more)
+                log_it(L_ERROR, "Token refresh datum %s contains a non-valid TSD section. "
+                                "The size of the TSD section and the offset exceed the set size of the TSD sections.", a_token_update->ticker);
+            return false;
+        }
+        switch (l_tsd->type) {
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID:
+                l_quantity_tsd_section_edit_signs_emission++;
+                l_tsd_signs_valid = l_tsd;
+                break;
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE:
+                l_quantity_tsd_remote_pkeys++;
+                l_tsd_list_remote_pkeys = dap_list_append(l_tsd_list_remote_pkeys, l_tsd);
+                break;
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD:
+                l_quantity_tsd_add_pkeys++;
+                l_tsd_list_added_pkeys = dap_list_append(l_tsd_list_added_pkeys, l_tsd);
+                break;
+        }
+        l_tsd_offset += l_tsd_size;
+    }
+    if (l_quantity_tsd_section_edit_signs_emission > 1) {
+        if (s_debug_more) {
+            log_it(L_ERROR, "Datum contains %ud TSD sections of type DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID which is not true. "
+                            "There can be at most one such TSD section.", l_quantity_tsd_section_edit_signs_emission);
+        }
+        dap_list_free1(l_tsd_list_added_pkeys);
+        dap_list_free1(l_tsd_list_remote_pkeys);
+        return false;
+    }
+    //Check new count signs
+    size_t l_new_signs_total = auth_signs_total + l_quantity_tsd_add_pkeys - l_quantity_tsd_remote_pkeys;
+    if (l_tsd_signs_valid) {
+        size_t l_signs_valid_from_tsd = (size_t)(dap_tsd_get_scalar(l_tsd_signs_valid,uint16_t));
+        if (l_new_signs_total < l_signs_valid_from_tsd || l_signs_valid_from_tsd < 1) {
+            dap_list_free1(l_tsd_list_added_pkeys);
+            dap_list_free1(l_tsd_list_remote_pkeys);
+            return false;
+        }
+    } else {
+        if (l_new_signs_total < auth_signs_valid){
+            dap_list_free1(l_tsd_list_added_pkeys);
+            dap_list_free1(l_tsd_list_remote_pkeys);
+            return false;
+        }
+    }
+    //Check valid remove_signs
+    bool isAccepted = false;
+    if (!l_tsd_list_remote_pkeys)
+        isAccepted = true;
+    else {
+        for (dap_list_t *l_ptr = l_tsd_list_remote_pkeys; l_ptr; l_ptr = dap_list_next(l_ptr)) {
+            dap_tsd_t *l_tsd = (dap_tsd_t *) l_ptr->data;
+            dap_hash_fast_t l_hash = dap_tsd_get_scalar(l_tsd, dap_hash_fast_t);
+            bool accepted = false;
+            for (size_t i = 0; i < auth_signs_total; i++) {
+                if (dap_hash_fast_compare(&a_cur_token_item->auth_pkeys_hash[i], &l_hash)) {
+                    accepted = true;
+                    break;
+                }
+            }
+            if (!accepted) {
+                if (s_debug_more) {
+                    char *l_hash_str = dap_hash_fast_to_str_new(&l_hash);
+                    log_it(L_ERROR,
+                           "It is expected that the TSD parameter DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE will contain only "
+                           "the hashes of the public keys of the signatures with which the given token was previously signed. But not %s",
+                           l_hash_str);
+                    DAP_DELETE(l_hash_str);
+                }
+            }
+            isAccepted = accepted;
+        }
+    }
+    if (!isAccepted) {
+        dap_list_free1(l_tsd_list_added_pkeys);
+        dap_list_free1(l_tsd_list_remote_pkeys);
+        return false;
+    }
+    //Check added signs
+    dap_chain_datum_token_t *l_token_tmp = DAP_DUP_SIZE(a_token_update, a_token_update_size);
+    l_token_tmp->header_native_update.tsd_total_size = 0;
+    isAccepted = true;
+    for (dap_list_t *l_ptr = l_tsd_list_added_pkeys; l_ptr; l_ptr = dap_list_next(l_ptr)) {
+        dap_tsd_t *l_tsd = (dap_tsd_t*)l_ptr->data;
+        if (l_tsd->size >= sizeof(dap_pkey_t)) {
+            dap_pkey_t *l_pkey = (dap_pkey_t *) l_tsd->data;
+            dap_hash_fast_t l_hf_pkey = {0};
+            if (!dap_pkey_get_hash(l_pkey, &l_hf_pkey)) {
+                if (s_debug_more)
+                    log_it(L_ERROR, "Failed to calculate the hash for the public key located in the "
+                                    "DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD section of the TSD");
+                isAccepted = false;
+                break;
+            }
+            for (size_t i = 0; i < a_cur_token_item->auth_signs_total; i++) {
+                if (dap_hash_fast_compare(&l_hf_pkey, &a_cur_token_item->auth_pkeys_hash[i])) {
+                    if (s_debug_more) {
+                        char *l_hf_str = dap_hash_fast_to_str_new(&l_hf_pkey);
+                        log_it(L_ERROR, "The public key with hash %s from the TSD section of the type "
+                                        "DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD cannot be added, because such "
+                                        "a key already exists in the ledger.", l_hf_str);
+                        DAP_DELETE(l_hf_str);
+                    }
+                    isAccepted = false;
+                    break;
+                }
+            }
+        } else {
+            if (s_debug_more)
+                log_it(L_ERROR, "It is expected that the size %zu of information from the TSD section of type "
+                                "DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD will be greater than or equal to %zu.",
+                       dap_tsd_size(l_tsd), sizeof(dap_pkey_t));
+            isAccepted = false;
+            break;
+        }
+    }
+    dap_list_free1(l_tsd_list_added_pkeys);
+    dap_list_free1(l_tsd_list_remote_pkeys);
+    DAP_DELETE(l_token_tmp);
+    return isAccepted;
 }
 
 
@@ -907,14 +1060,15 @@ int dap_chain_ledger_token_add(dap_ledger_t *a_ledger, dap_chain_datum_token_t *
         l_token_item->current_supply = l_token_item->total_supply;
         l_token_item->auth_signs_total = l_token->signs_total;
         l_token_item->auth_signs_valid = l_token->signs_valid;
-        l_token_item->auth_signs = dap_chain_datum_token_signs_parse(a_token, a_token_size,
+        dap_sign_t **l_signs = dap_chain_datum_token_signs_parse(a_token, a_token_size,
                                                                      &l_token_item->auth_signs_total,
                                                                      &l_token_item->auth_signs_valid);
         if (l_token_item->auth_signs_total) {
-            l_token_item->auth_signs_pkey_hash = DAP_NEW_Z_SIZE(dap_chain_hash_fast_t, sizeof(dap_chain_hash_fast_t) *
-                                                                                       l_token_item->auth_signs_total);
+            l_token_item->auth_pkeys = DAP_NEW_Z_SIZE(dap_pkey_t*, sizeof(dap_pkey_t*) * l_token_item->auth_signs_total);
+            l_token_item->auth_pkeys_hash = DAP_NEW_Z_SIZE(dap_chain_hash_fast_t, sizeof(dap_chain_hash_fast_t) * l_token_item->auth_signs_total);;
             for (uint16_t k = 0; k < l_token_item->auth_signs_total; k++) {
-                dap_sign_get_pkey_hash(l_token_item->auth_signs[k], &l_token_item->auth_signs_pkey_hash[k]);
+                l_token_item->auth_pkeys[k] = dap_pkey_get_from_sign_deserialization(l_signs[k]);
+                dap_pkey_get_hash(l_token_item->auth_pkeys[k], &l_token_item->auth_pkeys_hash[k]);
             }
         }
     }
@@ -994,6 +1148,7 @@ int dap_chain_ledger_token_add(dap_ledger_t *a_ledger, dap_chain_datum_token_t *
                         DAP_DEL_Z(l_balance);
                     }
                     l_res_token_tsd_parse = s_token_tsd_parse(a_ledger, l_token_item, l_token, l_token_size);
+                    s_tsd_sign_apply(a_ledger, l_token_item, a_token, a_token_size);
                 }
                     break;
                 case DAP_CHAIN_DATUM_TOKEN_SUBTYPE_NATIVE: {
@@ -1006,6 +1161,7 @@ int dap_chain_ledger_token_add(dap_ledger_t *a_ledger, dap_chain_datum_token_t *
                         DAP_DEL_Z(l_balance);
                     }
                     l_res_token_tsd_parse = s_token_tsd_parse(a_ledger, l_token_item, l_token, l_token_size);
+                    s_tsd_sign_apply(a_ledger, l_token_item, a_token, a_token_size);
                 }
                     break;
             }
@@ -1084,48 +1240,6 @@ static int s_token_tsd_parse(dap_ledger_t * a_ledger, dap_chain_ledger_token_ite
                 a_token_item->auth_signs_valid = dap_tsd_get_scalar(l_tsd,uint16_t);
             }break;
 
-            // Remove owner signature by pkey fingerprint
-            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE:{
-                dap_hash_fast_t l_hash = dap_tsd_get_scalar(l_tsd,dap_hash_fast_t);
-                for( size_t i=0; i<a_token_item->auth_signs_total; i++){
-                    if (dap_hash_fast_compare(&l_hash, &a_token_item->auth_signs_pkey_hash[i] )){
-                        if (i+1 != a_token_item->auth_signs_total){
-                            memmove(a_token_item->auth_signs+i,a_token_item->auth_signs+i+1,
-                                   (a_token_item->auth_signs_total-i-1)*sizeof (void*));
-                            memmove(a_token_item->auth_signs_pkey_hash+i,a_token_item->auth_signs_pkey_hash+i+1,
-                                   (a_token_item->auth_signs_total-i-1)*sizeof (void*));
-                        }
-                        a_token_item->auth_signs_total--;
-                        if(a_token_item->auth_signs_total){
-                            // Type sizeof's misunderstanding in realloc?
-                            a_token_item->auth_signs = DAP_REALLOC(a_token_item->auth_signs,a_token_item->auth_signs_total*sizeof (void*) );
-                            a_token_item->auth_signs_pkey_hash = DAP_REALLOC(a_token_item->auth_signs_pkey_hash,a_token_item->auth_signs_total*sizeof (void*) );
-                        }else{
-                            DAP_DEL_Z(a_token_item->auth_signs);
-                            DAP_DEL_Z(a_token_item->auth_signs_pkey_hash);
-                        }
-
-                        break;
-                    }
-                }
-            }break;
-
-            // Add owner signature's pkey fingerprint
-            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD:{
-                if(l_tsd->size == sizeof (dap_hash_fast_t) ){
-                    a_token_item->auth_signs_total++;
-                    // Type sizeof's misunderstanding in realloc?
-                    a_token_item->auth_signs = DAP_REALLOC(a_token_item->auth_signs,a_token_item->auth_signs_total*sizeof (void*) );
-                    a_token_item->auth_signs_pkey_hash = DAP_REALLOC(a_token_item->auth_signs_pkey_hash,a_token_item->auth_signs_total*sizeof (void*) );
-                    a_token_item->auth_signs[a_token_item->auth_signs_total-1] = NULL;
-                    memcpy( &a_token_item->auth_signs_pkey_hash[a_token_item->auth_signs_total-1], l_tsd->data, l_tsd->size ) ;
-                }else{
-                    if(s_debug_more)
-                        log_it(L_ERROR,"TSD param DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD expected to have %zd bytes data length, not %zd",
-                           sizeof (dap_hash_fast_t), l_tsd_size );
-                }
-            }break;
-
             //Allowed tx receiver addres list add, remove or clear
             case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_ADD:{
                 if( l_tsd->size == sizeof (dap_chain_addr_t) ){
@@ -1488,6 +1602,78 @@ static int s_token_tsd_parse(dap_ledger_t * a_ledger, dap_chain_ledger_token_ite
     return 0;
 }
 
+static int s_tsd_sign_apply(dap_ledger_t *a_ledger, dap_chain_ledger_token_item_t *a_token_item , dap_chain_datum_token_t *a_token, size_t a_token_size){
+    dap_tsd_t * l_tsd= dap_chain_datum_token_tsd_get(a_token,a_token_size);
+    size_t l_tsd_size=0;
+    size_t l_tsd_total_size = a_token->header_native_decl.tsd_total_size;
+    dap_tsd_t *l_new_signs_valid = NULL;
+    dap_list_t *l_remove_pkeys = NULL;
+    dap_list_t *l_added_pkeys = NULL;
+
+    for( size_t l_offset=0; l_offset < l_tsd_total_size;  l_offset += l_tsd_size ){
+        l_tsd = (dap_tsd_t *) (((byte_t*)l_tsd) + l_tsd_size);
+        l_tsd_size =  l_tsd? dap_tsd_size(l_tsd): 0;
+        if( l_tsd_size==0 ){
+            if(s_debug_more)
+                log_it(L_ERROR,"Wrong zero TSD size, exiting TSD parse");
+            break;
+        }else if (l_tsd_size + l_offset > l_tsd_total_size ){
+            if(s_debug_more)
+                log_it(L_ERROR,"Wrong %zd TSD size, exiting TSD parse", l_tsd_size);
+            break;
+        }
+        switch (l_tsd->type) {
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID:
+                l_new_signs_valid = l_tsd;
+                break;
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD:
+                l_added_pkeys = dap_list_append(l_added_pkeys, l_tsd->data);
+                break;
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE:
+                l_remove_pkeys = dap_list_append(l_remove_pkeys, l_tsd);
+                break;
+        }
+    }
+    for (dap_list_t *l_ptr = l_remove_pkeys; l_ptr; l_ptr = dap_list_next(l_ptr)) {
+        dap_tsd_t *l_tsd = l_ptr->data;
+        dap_hash_fast_t l_hash = dap_tsd_get_scalar(l_tsd, dap_chain_hash_fast_t);
+        for( size_t i=0; i<a_token_item->auth_signs_total; i++){
+            if (dap_hash_fast_compare(&l_hash, &a_token_item->auth_pkeys_hash[i] )){
+                if (i+1 != a_token_item->auth_signs_total){
+                    memmove(a_token_item->auth_pkeys+i,a_token_item->auth_pkeys+i+1,
+                            (a_token_item->auth_signs_total-i-1)*sizeof (void*));
+                    memmove(a_token_item->auth_pkeys_hash+i,a_token_item->auth_pkeys_hash+i+1,
+                            (a_token_item->auth_signs_total-i-1)*sizeof(dap_chain_hash_fast_t));
+                }
+                a_token_item->auth_signs_total--;
+                if(a_token_item->auth_signs_total) {
+                    // Type sizeof's misunderstanding in realloc?
+                    a_token_item->auth_pkeys = DAP_REALLOC(a_token_item->auth_pkeys,a_token_item->auth_signs_total*sizeof (dap_pkey_t*) );
+                    a_token_item->auth_pkeys_hash = DAP_REALLOC(a_token_item->auth_pkeys_hash,a_token_item->auth_signs_total*sizeof(dap_chain_hash_fast_t));
+                } else {
+                    DAP_DEL_Z(a_token_item->auth_pkeys);
+                    DAP_DEL_Z(a_token_item->auth_pkeys_hash);
+                }
+                break;
+            }
+        }
+    }
+    for (dap_list_t *l_ptr = l_added_pkeys; l_ptr; l_ptr = dap_list_next(l_ptr)) {
+        dap_pkey_t *l_pkey = (dap_pkey_t*)l_ptr->data;
+        a_token_item->auth_signs_total++;
+        // Type sizeof's misunderstanding in realloc?
+        a_token_item->auth_pkeys = DAP_REALLOC(a_token_item->auth_pkeys,a_token_item->auth_signs_total*sizeof (dap_pkey_t*) );
+        a_token_item->auth_pkeys_hash = DAP_REALLOC(a_token_item->auth_pkeys_hash,a_token_item->auth_signs_total*sizeof (dap_chain_hash_fast_t));
+        a_token_item->auth_pkeys[a_token_item->auth_signs_total-1] = DAP_NEW_SIZE(dap_pkey_t, sizeof(dap_pkey_t)+l_pkey->header.size);
+        memcpy(a_token_item->auth_pkeys[a_token_item->auth_signs_total-1], l_pkey, sizeof(dap_pkey_t)+l_pkey->header.size);
+        dap_pkey_get_hash(l_pkey, &a_token_item->auth_pkeys_hash[a_token_item->auth_signs_total-1]);
+    }
+    if (l_new_signs_valid) {
+        a_token_item->auth_signs_valid = dap_tsd_get_scalar(l_new_signs_valid,uint16_t);
+    }
+    return 0;
+}
+
 int dap_chain_ledger_token_load(dap_ledger_t *a_ledger, dap_chain_datum_token_t *a_token, size_t a_token_size)
 {
     if (PVT(a_ledger)->load_mode) {
@@ -1664,7 +1850,7 @@ size_t dap_chain_ledger_token_auth_signs_total(dap_ledger_t *a_ledger, const cha
  * @param a_token_ticker
  * @return
  */
-dap_list_t * dap_chain_ledger_token_auth_signs_hashes(dap_ledger_t *a_ledger, const char * a_token_ticker)
+dap_list_t * dap_chain_ledger_token_auth_pkeys_hashes(dap_ledger_t *a_ledger, const char * a_token_ticker)
 {
     dap_list_t * l_ret = NULL;
     dap_chain_ledger_token_item_t *l_token_item, *l_tmp_item;
@@ -1673,7 +1859,7 @@ dap_list_t * dap_chain_ledger_token_auth_signs_hashes(dap_ledger_t *a_ledger, co
         if (!dap_strcmp(l_token_item->ticker, a_token_ticker)) {
             debug_if(s_debug_more, L_INFO, " ! Token %s : total %lu auth signs", a_token_ticker, l_token_item->auth_signs_total);
             for (size_t i = 0; i < l_token_item->auth_signs_total; i++) {
-                l_ret = dap_list_append(l_ret, (dap_chain_hash_fast_t*)(&l_token_item->auth_signs_pkey_hash[i]));
+                l_ret = dap_list_append(l_ret, (dap_chain_hash_fast_t*)(&l_token_item->auth_pkeys_hash[i]));
             }
             break;
         }
@@ -2300,7 +2486,7 @@ int dap_chain_ledger_token_emission_add_check(dap_ledger_t *a_ledger, byte_t *a_
                         dap_sign_get_pkey_hash(l_sign, &l_sign_pkey_hash);
                         // Find pkey in auth hashes
                         for (uint16_t k=0; k< l_token_item->auth_signs_total; k++) {
-                            if (dap_hash_fast_compare(&l_sign_pkey_hash, &l_token_item->auth_signs_pkey_hash[k])) {
+                            if (dap_hash_fast_compare(&l_sign_pkey_hash, &l_token_item->auth_pkeys_hash[k])) {
                                 // Verify if its token emission header signed
                                 if (dap_sign_verify(l_sign, l_emi_ptr_check_size, l_sign_data_check_size) == 1) {
                                     l_aproves++;
@@ -4397,8 +4583,8 @@ void dap_chain_ledger_purge(dap_ledger_t *a_ledger, bool a_preserve_db)
         }
         pthread_rwlock_unlock(&l_token_current->token_emissions_rwlock);
         DAP_DELETE(l_token_current->datum_token);
-        DAP_DELETE(l_token_current->auth_signs);
-        DAP_DELETE(l_token_current->auth_signs_pkey_hash);
+        DAP_DELETE(l_token_current->auth_pkeys);
+        DAP_DELETE(l_token_current->auth_pkeys_hash);
         DAP_DEL_Z(l_token_current->tx_recv_allow);
         DAP_DEL_Z(l_token_current->tx_recv_block);
         DAP_DEL_Z(l_token_current->tx_send_allow);
diff --git a/modules/chain/include/dap_chain_ledger.h b/modules/chain/include/dap_chain_ledger.h
index 2fdd38b722..daf04624b0 100644
--- a/modules/chain/include/dap_chain_ledger.h
+++ b/modules/chain/include/dap_chain_ledger.h
@@ -154,7 +154,7 @@ dap_string_t *dap_chain_ledger_balance_info(dap_ledger_t *a_ledger);
 
 size_t dap_chain_ledger_token_auth_signs_valid(dap_ledger_t *a_ledger, const char * a_token_ticker);
 size_t dap_chain_ledger_token_auth_signs_total(dap_ledger_t *a_ledger, const char * a_token_ticker);
-dap_list_t * dap_chain_ledger_token_auth_signs_hashes(dap_ledger_t *a_ledger, const char * a_token_ticker);
+dap_list_t * dap_chain_ledger_token_auth_pkeys_hashes(dap_ledger_t *a_ledger, const char * a_token_ticker);
 
 /**
  * Add token emission datum
diff --git a/modules/common/dap_chain_datum.c b/modules/common/dap_chain_datum.c
index 85c1d4ca1c..da193b7272 100644
--- a/modules/common/dap_chain_datum.c
+++ b/modules/common/dap_chain_datum.c
@@ -115,25 +115,33 @@ void s_datum_token_dump_tsd(dap_string_t *a_str_out, dap_chain_datum_token_t *a_
                 dap_string_append_printf(a_str_out,"total_signs_valid: %u\n",
                                          dap_tsd_get_scalar(l_tsd, uint16_t) );
             break;
-            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD :
-                if(l_tsd->size == sizeof(dap_chain_hash_fast_t) ){
-                    char *l_hash_str = (!dap_strcmp(a_hash_out_type, "hex") || !dap_strcmp(a_hash_out_type, "content_hash"))
-                            ? dap_chain_hash_fast_to_str_new((dap_chain_hash_fast_t*) l_tsd->data)
-                            : dap_enc_base58_encode_hash_to_str((dap_chain_hash_fast_t*) l_tsd->data);
-                    dap_string_append_printf(a_str_out,"total_signs_add: %s\n", l_hash_str);
-                    DAP_DELETE( l_hash_str );
-                }else
-                    dap_string_append_printf(a_str_out,"total_signs_add: <WRONG SIZE %u>\n", l_tsd->size);
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD:
+                if(l_tsd->size >= sizeof(dap_pkey_t)){
+                    char *l_hash_str;
+                    dap_pkey_t *l_pkey = (dap_pkey_t*)l_tsd->data;
+                    dap_hash_fast_t l_hf = {0};
+                    if (!dap_pkey_get_hash(l_pkey, &l_hf)) {
+                        dap_string_append_printf(a_str_out,"total_pkeys_add: <WRONG CALCULATION FINGERPRINT>\n");
+                    } else {
+                        if (!dap_strcmp(a_hash_out_type, "hex") || !dap_strcmp(a_hash_out_type, "content_hash"))
+                            l_hash_str = dap_chain_hash_fast_to_str_new(&l_hf);
+                        else
+                            l_hash_str = dap_enc_base58_encode_hash_to_str(&l_hf);
+                        dap_string_append_printf(a_str_out, "total_pkeys_add: %s\n", l_hash_str);
+                        DAP_DELETE(l_hash_str);
+                    }
+                } else
+                    dap_string_append_printf(a_str_out,"total_pkeys_add: <WRONG SIZE %u>\n", l_tsd->size);
             break;
-            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE :
+            case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE:
                 if(l_tsd->size == sizeof(dap_chain_hash_fast_t) ){
                     char *l_hash_str = (!dap_strcmp(a_hash_out_type,"hex")|| !dap_strcmp(a_hash_out_type, "content_hash"))
                             ? dap_chain_hash_fast_to_str_new((dap_chain_hash_fast_t*) l_tsd->data)
                             : dap_enc_base58_encode_hash_to_str((dap_chain_hash_fast_t*) l_tsd->data);
-                    dap_string_append_printf(a_str_out,"total_signs_remove: %s\n", l_hash_str );
+                    dap_string_append_printf(a_str_out,"total_pkeys_remove: %s\n", l_hash_str);
                     DAP_DELETE( l_hash_str );
                 }else
-                    dap_string_append_printf(a_str_out,"total_signs_add: <WRONG SIZE %u>\n", l_tsd->size);
+                    dap_string_append_printf(a_str_out,"total_pkeys_remove: <WRONG SIZE %u>\n", l_tsd->size);
             break;
             case DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DELEGATE_EMISSION_FROM_STAKE_LOCK: {
                 char *balance = NULL;
diff --git a/modules/common/include/dap_chain_datum_token.h b/modules/common/include/dap_chain_datum_token.h
index 0ae88809c7..37210a4d2a 100644
--- a/modules/common/include/dap_chain_datum_token.h
+++ b/modules/common/include/dap_chain_datum_token.h
@@ -258,10 +258,10 @@ extern const char *c_dap_chain_datum_token_flag_str[];
 #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID                    0x0004
 
 // Remove owner signature by pkey fingerprint
-#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE                   0x0005
+#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE                   0x0005
 
 // Add owner signature's pkey fingerprint
-#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD                      0x0006
+#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD                      0x0006
 
 // Emission for delegated token
 #define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DELEGATE_EMISSION_FROM_STAKE_LOCK	0x0027
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 8c3355f0ae..95917a8314 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -225,6 +225,12 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
             "\n"
             );
 
+    dap_cli_server_cmd_add("token_update_sign", com_token_decl_sign, "Token update add sign and new sign",
+                                        "token_update_sign -net <net_name> -chain <chain_name> -datum <datum_hash> -certs <certs list> -new_certs <certs list>\n"
+                                        "\t Sign existent <datum hash> in mempool with <certs list>\n"
+    );
+    // Token commands
+
     dap_cli_server_cmd_add ("token_decl_sign", com_token_decl_sign, "Token declaration add sign",
             "token_decl_sign -net <net_name> -chain <chain_name> -datum <datum_hash> -certs <certs list>\n"
             "\t Sign existent <datum hash> in mempool with <certs list>\n"
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index ef2ba6ef5e..86bb99d626 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -2256,7 +2256,7 @@ static dap_chain_datum_token_t * s_sign_cert_in_cycle(dap_cert_t ** l_certs, dap
 
         if (l_sign) {
             size_t l_sign_size = dap_sign_get_size(l_sign);
-            l_datum_token = DAP_REALLOC(l_datum_token, sizeof(dap_chain_datum_token_t) + *l_datum_signs_offset + l_sign_size);
+            l_datum_token = DAP_REALLOC(l_datum_token, sizeof(dap_chain_datum_token_t) + (*l_datum_signs_offset) + l_sign_size);
             memcpy(l_datum_token->data_n_tsd + *l_datum_signs_offset, l_sign, l_sign_size);
             *l_datum_signs_offset += l_sign_size;
             DAP_DELETE(l_sign);
@@ -2352,7 +2352,8 @@ int com_token_decl_sign(int a_argc, char **a_argv, char ** a_str_reply)
                 l_datum_hash_hex_str, &l_datum_size, NULL, NULL )) != NULL) {
 
             // Check if its token declaration
-            if(l_datum->header.type_id == DAP_CHAIN_DATUM_TOKEN_DECL) {
+            if(l_datum->header.type_id == DAP_CHAIN_DATUM_TOKEN_DECL ||
+                l_datum->header.type_id == DAP_CHAIN_DATUM_TOKEN_TYPE_UPDATE) {
                 dap_chain_datum_token_t *l_datum_token = DAP_DUP_SIZE(l_datum->data, l_datum->header.data_size);    // for realloc
                 DAP_DELETE(l_datum);
                 if ((l_datum_token->subtype == DAP_CHAIN_DATUM_TOKEN_SUBTYPE_PRIVATE)
@@ -3363,9 +3364,48 @@ static int s_parse_additional_token_decl_arg(int a_argc, char ** a_argv, char **
     if (a_params->ext.tx_sender_blocked)
         l_tsd_list = s_parse_wallet_addresses(a_params->ext.tx_sender_blocked, l_tsd_list, &l_tsd_total_size, DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_ADD);
 
-    if (!l_tsd_total_size)
-        return 0;
-
+    const char* l_new_certs_str = NULL;
+    const char* l_remove_signs = NULL;
+    dap_cli_server_cmd_find_option_val(a_argv, 0, a_argc, "-new_certs", &l_new_certs_str);
+    dap_cli_server_cmd_find_option_val(a_argv, 0, a_argc, "-remove_certs", &l_remove_signs);
+
+    //Added remove signs
+    if (l_remove_signs) {
+        size_t l_added_tsd_size = 0;
+        char *l_remove_signs_ptrs = NULL;
+        char *l_remove_signs_dup = strdup(l_remove_signs);
+        char *l_remove_signs_str = strtok_r(l_remove_signs_dup, ",", &l_remove_signs_ptrs);
+        for (; l_remove_signs_str; l_remove_signs_str = strtok_r(NULL, ",", &l_remove_signs_ptrs)) {
+            dap_hash_fast_t *l_hf = DAP_NEW(dap_hash_fast_t);
+            char *l_tmp = strdup(l_remove_signs_str);
+            if (dap_chain_hash_fast_from_str(l_tmp, l_hf) == 0) {
+                dap_tsd_t *l_hf_tsd = dap_tsd_create(DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_REMOVE, l_hf, sizeof(dap_hash_fast_t));
+                size_t l_hf_tsd_size = dap_tsd_size(l_hf_tsd);
+                l_tsd_list = dap_list_append(l_tsd_list, l_hf_tsd);
+                l_added_tsd_size += l_hf_tsd_size;
+            }
+            DAP_DELETE(l_hf);
+            DAP_DELETE(l_tmp);
+        }
+        DAP_DELETE(l_remove_signs_dup);
+        DAP_DELETE(l_remove_signs_str);
+        l_tsd_total_size += l_added_tsd_size;
+    }
+    //Added new certs
+    dap_cert_t **l_new_certs = NULL;
+    size_t l_new_certs_count = 0;
+    dap_cert_parse_str_list(l_new_certs_str, &l_new_certs, &l_new_certs_count);
+    for (size_t i=0; i < l_new_certs_count; i++){
+        dap_pkey_t *l_pkey = dap_cert_to_pkey(l_new_certs[i]);
+        size_t l_pkey_size = sizeof(dap_pkey_t) + l_pkey->header.size;
+        dap_tsd_t *l_pkey_tsd = dap_tsd_create(DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_PKEYS_ADD, l_pkey, l_pkey_size);
+        size_t l_pkey_tsd_size = dap_tsd_size(l_pkey_tsd);
+        l_tsd_list = dap_list_append(l_tsd_list, l_pkey_tsd);
+        l_tsd_total_size += l_pkey_tsd_size;
+        DAP_DELETE(l_pkey);
+        DAP_DELETE(l_new_certs[i]);
+    }
+    DAP_DELETE(l_new_certs);
     size_t l_tsd_offset = 0;
     a_params->ext.parsed_tsd = DAP_NEW_SIZE(byte_t, l_tsd_total_size);
     for (dap_list_t *l_iter = dap_list_first(l_tsd_list); l_iter; l_iter = l_iter->next) {
diff --git a/modules/net/include/dap_chain_node_cli_cmd.h b/modules/net/include/dap_chain_node_cli_cmd.h
index d42ec09455..48eb99f2d0 100644
--- a/modules/net/include/dap_chain_node_cli_cmd.h
+++ b/modules/net/include/dap_chain_node_cli_cmd.h
@@ -91,6 +91,11 @@ int com_token_update(int a_argc, char **a_argv, char ** a_str_reply);
  */
 int com_token_decl_sign ( int a_argc,  char **a_argv, char ** str_reply);
 
+/*
+ * Token update sign
+ */
+int com_token_update_sign(int argc, char ** argv, char ** a_str_reply);
+
 /**
  * Token emission
  */
-- 
GitLab