diff --git a/src/knot/dnssec/nsec-chain.c b/src/knot/dnssec/nsec-chain.c
index 495007d3dc63323f1307fa939ed575bedeb9aebc..9b0429c323b6fc243cefebef36e1cc84adcf18ed 100644
--- a/src/knot/dnssec/nsec-chain.c
+++ b/src/knot/dnssec/nsec-chain.c
@@ -23,110 +23,6 @@
 #include "knot/dnssec/zone-sign.h"
 #include "knot/dnssec/zone-nsec.h"
 
-/* - Forward declarations --------------------------------------------------- */
-
-static knot_rrset_t *create_nsec_rrset(const knot_node_t *,
-                                       const knot_node_t *,
-                                       uint32_t);
-
-/* - Helper functions ------------------------------------------------------- */
-
-/*!
- * \brief Returns true if NSEC is only RRSet in node.
- */
-static bool only_nsec_in_node(const knot_node_t *n)
-{
-	assert(n);
-	return n->rrset_count <= 2 && (knot_node_rrset(n, KNOT_RRTYPE_NSEC)
-	       && knot_node_rrset(n, KNOT_RRTYPE_RRSIG));
-}
-
-/*!
- * \brief Updates last used node and DNAME.
- *
- * \param data  Data to be updated.
- * \param d     DNAME to be set.
- * \param n     Node to be set.
- */
-static void update_last_used(chain_fix_data_t *data, const knot_dname_t *d,
-                             const knot_node_t *n)
-{
-	assert(data && d);
-	data->last_used_dname = d;
-	data->last_used_node = n;
-}
-
-/*!
- * \brief Checks whether NSEC in zone is valid and updates it if needed.
- *
- * \param from     Start node for NSEC link.
- * \param to       End node for NSEC link.
- * \param out_ch   Changes are stored here.
- * \param soa_min  TTL to use for NSEC RRs.
- *
- * \return KNOT_E*
- */
-static int update_nsec(const knot_node_t *from, const knot_node_t *to,
-                       knot_changeset_t *out_ch, uint32_t soa_min)
-{
-	assert(from && to && out_ch);
-	const knot_rrset_t *nsec_rrset = knot_node_rrset(from,
-	                                                 KNOT_RRTYPE_NSEC);
-	// Create new NSEC
-	knot_rrset_t *new_nsec;
-	if (only_nsec_in_node(from)) {
-		// Just NSEC present, it has to be dropped
-		new_nsec = NULL;
-	} else {
-		new_nsec = create_nsec_rrset(from, to, soa_min);
-		if (new_nsec == NULL) {
-			return KNOT_ERROR;
-		}
-	}
-
-	// If node in zone has NSEC record, drop it if needed
-	if (nsec_rrset && new_nsec) {
-		if (!knot_rrset_equal(new_nsec, nsec_rrset,
-		                      KNOT_RRSET_COMPARE_WHOLE)) {
-			dbg_dnssec_detail("Creating new NSEC for %s\n",
-			                  knot_dname_to_str(new_nsec->owner));
-			// Drop old
-			int ret = knot_nsec_changeset_remove(from, out_ch);
-			if (ret != KNOT_EOK) {
-				knot_rrset_deep_free(&new_nsec, 1, NULL);
-				return ret;
-			}
-			// Add new
-			ret = knot_changeset_add_rrset(out_ch, new_nsec,
-			                               KNOT_CHANGESET_ADD);
-			if (ret != KNOT_EOK) {
-				knot_rrset_deep_free(&new_nsec, 1, NULL);
-				return ret;
-			}
-		} else {
-			// All good, no need to update
-			knot_rrset_deep_free(&new_nsec, 1, NULL);
-			return KNOT_EOK;
-		}
-	} else if (new_nsec) {
-		// Add new NSEC record
-		int ret = knot_changeset_add_rrset(out_ch, new_nsec,
-		                                   KNOT_CHANGESET_ADD);
-		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&new_nsec, 1, NULL);
-			return ret;
-		}
-	} else {
-		// Drop old, no longer needed
-		int ret = knot_nsec_changeset_remove(from, out_ch);
-		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&new_nsec, 1, NULL);
-			return ret;
-		}
-	}
-	return KNOT_EOK;
-}
-
 /* - NSEC chain construction ------------------------------------------------ */
 
 /*!
@@ -253,245 +149,6 @@ static int connect_nsec_nodes(knot_node_t *a, knot_node_t *b,
 	                                KNOT_CHANGESET_ADD);
 }
 
-/* - NSEC chain fix --------------------------------------------------------- */
-
-/*!
- * \brief Handles node that has been deleted by DDNS/reload.
- *
- * \param node      Deleted node
- * \param fix_data  Chain fix data.
- *
- * \return KNOT_E*, NSEC_NODE_SKIP
- */
-static int handle_deleted_node(const knot_node_t *node,
-                               chain_fix_data_t *fix_data)
-{
-	if (node == NULL) {
-		// This node was deleted and used to be non-auth
-		assert(knot_node_is_non_auth(node));
-		return NSEC_NODE_SKIP;
-	}
-
-	int ret = knot_nsec_changeset_remove(node, fix_data->out_ch);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	/*!
-	 * This node should be ignored, but we might need the next dname from
-	 * previous node.
-	 */
-	if (fix_data->next_dname == NULL) {
-		const knot_rrset_t *old_nsec =
-			knot_node_rrset(node, KNOT_RRTYPE_NSEC);
-		assert(old_nsec);
-		fix_data->next_dname =
-			(knot_dname_t *)knot_rdata_nsec_next(old_nsec);
-		assert(fix_data->next_dname);
-	}
-
-	return NSEC_NODE_SKIP;
-}
-
-/*!
- * \brief Fixes 'gaps' between old and new NSEC chain.
- *
- * \param fix_data  Chain fix data.
- * \param a         Dname that should be connected to old chain.
- * \param a_node    Node that should be connected to old chain.
- *
- * \return KNOT_E*, or NSEC_NODE_RESET if needed.
- */
-static int handle_nsec_next_dname(chain_fix_data_t *fix_data,
-                                  const knot_dname_t *a,
-                                  const knot_node_t *a_node)
-{
-	assert(fix_data && fix_data->next_dname && a && a_node);
-	int ret = KNOT_EOK;
-	if (knot_dname_is_equal(fix_data->next_dname, a)) {
-		// We cannot point to the same record here, extract next->next
-		const knot_rrset_t *nsec_rrset =
-			knot_node_rrset(a_node, KNOT_RRTYPE_NSEC);
-		assert(nsec_rrset);
-		const knot_node_t *next_node =
-			knot_zone_contents_find_node(fix_data->zone,
-			                             knot_rdata_nsec_next(nsec_rrset));
-		assert(next_node);
-		update_last_used(fix_data, next_node->owner, next_node);
-		ret = update_nsec(a_node, next_node, fix_data->out_ch,
-		                  fix_data->ttl);
-	} else {
-		// We have no immediate previous node, connect broken chain
-		const knot_node_t *next_node =
-			knot_zone_contents_find_node(fix_data->zone,
-			                             fix_data->next_dname);
-		assert(next_node);
-		update_last_used(fix_data, next_node->owner, next_node);
-		ret = update_nsec(a_node, next_node, fix_data->out_ch,
-		                  fix_data->ttl);
-	}
-	fix_data->next_dname = NULL;
-	return ret == KNOT_EOK ? NSEC_NODE_RESET : ret;
-}
-
-/*!
- * \brief Finds previous usable NSEC node in zone.
- *
- * \param z  Zone to be searched.
- * \param d  DNAME to search for.
- *
- * \return Previous NSEC node for 'd'.
- */
-static const knot_node_t *find_prev_nsec_node(const knot_zone_contents_t *z,
-                                              const knot_dname_t *d)
-{
-	// Find previous node for the dname, return node that will be used later
-	const knot_node_t *prev_zone_node = knot_zone_contents_find_previous(z,
-	                                                                     d);
-	bool nsec_node_found = !knot_node_is_non_auth(prev_zone_node) &&
-	                       !only_nsec_in_node(prev_zone_node);
-	while (!nsec_node_found) {
-		// Get previous node from zone tree
-		prev_zone_node =
-			knot_zone_contents_find_previous(z,
-		                                         prev_zone_node->owner);
-		assert(prev_zone_node);
-		// Infinite loop check
-		if (knot_dname_is_equal(d, prev_zone_node->owner)) {
-				return prev_zone_node;
-		}
-		nsec_node_found = !knot_node_is_non_auth(prev_zone_node) &&
-		                  !only_nsec_in_node(prev_zone_node);
-	}
-	assert(nsec_node_found);
-	return prev_zone_node;
-}
-
-/*!
- * \brief Fixes NSEC chain for 'a' and 'b'. 'a' is always < 'b'.
- *
- * \param a         First DNAME from changeset.
- * \param b         Second DNAME from changeset.
- * \param fix_data  Chain fix data.
- *
- * \return KNOT_E*, NSEC_NODE_SKIP, NSEC_NODE_RESET if needed.
- */
-static int fix_nsec_chain(knot_dname_t *a, knot_dname_t *b,
-                          chain_fix_data_t *fix_data)
-{
-	assert(b);
-	assert(fix_data);
-	// Get changed nodes from zone
-	const knot_node_t *b_node = knot_zone_contents_find_node(fix_data->zone,
-	                                                         b);
-	assert(b_node);
-	if (knot_node_is_non_auth(b_node)) {
-		// Nothing to fix in this node
-		return NSEC_NODE_SKIP;
-	}
-	const knot_node_t *a_node = knot_zone_contents_find_node(fix_data->zone,
-	                                                         a);
-	// Find previous node in zone
-	const knot_node_t *prev_zone_node = find_prev_nsec_node(fix_data->zone,
-	                                                        b);
-	if (prev_zone_node == NULL) {
-		return KNOT_ERROR;
-	}
-
-	// Handle removals
-	bool node_deleted = only_nsec_in_node(b_node);
-	if (node_deleted) {
-		/*!
-		 * If DDNS only contains removals, we need at least
-		 * one 'last_used_dname'.
-		 */
-		if (fix_data->last_used_dname == NULL) {
-			assert(fix_data->last_used_node == NULL);
-			update_last_used(fix_data, prev_zone_node->owner,
-			                 prev_zone_node);
-		}
-		return handle_deleted_node(b_node, fix_data);
-	}
-
-	// Find out whether the previous node is also part of the changeset.
-	bool dname_equal =
-		a && knot_dname_is_equal(prev_zone_node->owner, a);
-	if (dname_equal) {
-		// No valid data for the previous node, create the forward link
-		update_last_used(fix_data, b_node->owner, b_node);
-		return update_nsec(a_node, b_node, fix_data->out_ch,
-		                   fix_data->ttl);
-	} else {
-		// Use data from zone or next_dname
-		if (fix_data->next_dname) {
-			return handle_nsec_next_dname(fix_data, a, a_node);
-		}
-
-		// Previous node was not changed in DDNS, it has to have NSEC
-		const knot_rrset_t *nsec_rrset =
-			knot_node_rrset(prev_zone_node, KNOT_RRTYPE_NSEC);
-		assert(nsec_rrset);
-		const knot_node_t *next_node = b_node;
-
-		// Store next node for next iterations
-		fix_data->next_dname =
-			(knot_dname_t *)knot_rdata_nsec_next(nsec_rrset);
-		update_last_used(fix_data, next_node->owner, next_node);
-		// Fix NSEC
-		return update_nsec(prev_zone_node, next_node, fix_data->out_ch,
-		                   fix_data->ttl);
-	}
-
-	return KNOT_EOK;
-}
-
-/*!
- * \brief Wrapper for iteration function to be used with NSEC,
- *        shortens the code a bit.
- */
-static int fix_nsec_chain_wrap(knot_dname_t *a, knot_dname_t *a_hash,
-                               knot_dname_t *b, knot_dname_t *b_hash,
-                               chain_fix_data_t *d)
-{
-	UNUSED(a_hash);
-	UNUSED(b_hash);
-	return fix_nsec_chain(a, b, d);
-}
-
-/*!
- * \brief Finalizes NSEC chain.
- *
- * \param d  Fix data.
- *
- * \return KNOT_E*
- */
-static int chain_finalize_nsec(chain_fix_data_t *fix_data)
-{
-	assert(fix_data);
-	assert(fix_data->last_used_dname && fix_data->next_dname);
-	const knot_node_t *from = fix_data->last_used_node;
-	assert(from);
-	const knot_node_t *to = NULL;
-	if (knot_dname_is_equal(fix_data->last_used_dname,
-	                        fix_data->zone->apex->owner)) {
-		// Everything but the apex deleted
-		to = fix_data->zone->apex;
-	} else if (knot_dname_is_equal(fix_data->last_used_dname,
-	                               fix_data->next_dname)) {
-		// NSEC cannot point to itself (except for the case above)
-		const knot_rrset_t *nsec_rrset =
-			knot_node_rrset(from, KNOT_RRTYPE_NSEC);
-		to = knot_zone_contents_find_node(fix_data->zone,
-		                                  knot_rdata_nsec_next(nsec_rrset));
-	} else {
-		// Normal case
-		to = knot_zone_contents_find_node(fix_data->zone,
-		                                  fix_data->next_dname);
-	}
-	assert(to);
-	return update_nsec(from, to, fix_data->out_ch, fix_data->ttl);
-}
-
 /* - API - iterations ------------------------------------------------------- */
 
 /*!
@@ -545,68 +202,6 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes,
 	                 callback(current, first, data);
 }
 
-
-/*!
- * \brief Iterates sorted changeset and calls callback function - works for
- *        NSEC and NSEC3 chain.
- */
-int knot_nsec_chain_iterate_fix(hattrie_t *nodes, chain_iterate_fix_cb callback,
-                                chain_finalize_cb finalize,
-                                chain_fix_data_t *data)
-{
-	assert(nodes);
-	assert(callback);
-
-	bool sorted = true;
-	hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted);
-
-	if (!it) {
-		return KNOT_ENOMEM;
-	}
-
-	if (hattrie_iter_finished(it)) {
-		hattrie_iter_free(it);
-		return KNOT_EINVAL;
-	}
-
-	knot_dname_t *previous_original = NULL;
-	knot_dname_t *previous_hashed = NULL;
-	knot_dname_t *current_original = NULL;
-	knot_dname_t *current_hashed = NULL;
-
-	int result = KNOT_EOK;
-	while (!hattrie_iter_finished(it)) {
-		signed_info_t *val = (signed_info_t *)(*hattrie_iter_val(it));
-		current_original = val->dname;
-		current_hashed = val->hashed_dname;
-
-		result = callback(previous_original, previous_hashed,
-		                  current_original, current_hashed, data);
-		if (result == NSEC_NODE_SKIP) {
-			// No NSEC should be created for 'current' node, skip
-			hattrie_iter_next(it);
-		} else if (result == NSEC_NODE_RESET) {
-			/*!
-			 * Used previous node, call once again so that
-			 * we don't lose this current node.
-			 */
-			previous_original = NULL;
-			previous_hashed = NULL;
-		} else if (result == KNOT_EOK) {
-			previous_original = current_original;
-			previous_hashed = current_hashed;
-			hattrie_iter_next(it);
-		} else {
-			hattrie_iter_free(it);
-			return result;
-		}
-	}
-
-	hattrie_iter_free(it);
-
-	return finalize(data);
-}
-
 /* - API - utility functions ------------------------------------------------ */
 
 /*!
@@ -696,7 +291,7 @@ bool knot_nsec_only_nsec_and_rrsigs_in_node(const knot_node_t *n)
 	return true;
 }
 
-/* - API - Chain creation and fix ------------------------------------------- */
+/* - API - Chain creation --------------------------------------------------- */
 
 /*!
  * \brief Create new NSEC chain, add differences from current into a changeset.
@@ -714,17 +309,3 @@ int knot_nsec_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
 	                                      connect_nsec_nodes, &data);
 }
 
-/*!
- * \brief Fixes NSEC chain after DDNS/reload
- */
-int knot_nsec_fix_chain(hattrie_t *sorted_changes,
-                        chain_fix_data_t *fix_data)
-{
-	if (sorted_changes == NULL || fix_data == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	hattrie_build_index(sorted_changes);
-	return knot_nsec_chain_iterate_fix(sorted_changes, fix_nsec_chain_wrap,
-	                                   chain_finalize_nsec, fix_data);
-}
diff --git a/src/knot/dnssec/nsec-chain.h b/src/knot/dnssec/nsec-chain.h
index 39f0ac7304db5d801000d82f2282116d79553904..e4bce7004f52a7109cb65f81a290d3b8de2c2e68 100644
--- a/src/knot/dnssec/nsec-chain.h
+++ b/src/knot/dnssec/nsec-chain.h
@@ -17,7 +17,6 @@
  * \file nsec-chain.h
  *
  * \author Jan Vcelak <jan.vcelak@nic.cz> (chain creation)
- * \author Jan Kadlec <jan.kadlec@nic.cz> (chain fix)
  *
  * \brief NSEC chain fix and creation.
  *
@@ -35,21 +34,6 @@
 #include "knot/updates/changesets.h"
 #include "libknot/dnssec/bitmap.h"
 
-/*!
- * \brief Parameters to be used when fixing NSEC(3) chain.
- */
-typedef struct chain_fix_data {
-	const knot_zone_contents_t *zone;     // Zone to fix
-	knot_changeset_t *out_ch;             // Outgoing changes
-	const knot_dname_t *chain_start;      // Possible new starting node
-	bool old_connected;                   // Marks old start connection
-	const knot_dname_t *last_used_dname;  // Last dname used in chain
-	const knot_node_t *last_used_node;    // Last covered node used in chain
-	knot_dname_t *next_dname;             // Used to reconnect broken chain
-	const hattrie_t *sorted_changes;      // Iterated trie
-	uint32_t ttl;                         // TTL for NSEC(3) records
-} chain_fix_data_t;
-
 /*!
  * \brief Parameters to be used in connect_nsec_nodes callback.
  */
@@ -64,18 +48,7 @@ typedef struct {
  */
 enum {
 	NSEC_NODE_SKIP = 1,
-	NSEC_NODE_RESET = 2
 };
-/*!
- * \brief Callback used when fixing NSEC chains.
- */
-typedef int (*chain_iterate_fix_cb)(knot_dname_t *, knot_dname_t *,
-                                    knot_dname_t *, knot_dname_t *,
-                                    chain_fix_data_t *);
-/*!
- * \brief Callback used when finalizing NSEC chains.
- */
-typedef int (*chain_finalize_cb)(chain_fix_data_t *);
 
 /*!
  * \brief Callback used when creating NSEC chains.
@@ -116,25 +89,6 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes,
                                    chain_iterate_create_cb callback,
                                    nsec_chain_iterate_data_t *data);
 
-/*!
- * \brief Iterates sorted changeset and calls callback function - works for
- *        NSEC and NSEC3 chain.
- *
- * \note If the callback function returns anything other than KNOT_EOK, the
- *       iteration is terminated and the error code is propagated.
- *
- * \param  nodes     Tree to fix.
- * \param  callback  Callback to call.
- * \param  finalize  Finalization callback.
- * \param  data      Data needed for fixing.
- *
- * \return KNOT_E*
- */
-int knot_nsec_chain_iterate_fix(hattrie_t *nodes,
-                                chain_iterate_fix_cb callback,
-                                chain_finalize_cb finalize,
-                                chain_fix_data_t *data);
-
 /*!
  * \brief Add entry for removed NSEC(3) and its RRSIG to the changeset.
  *
@@ -169,16 +123,4 @@ bool knot_nsec_only_nsec_and_rrsigs_in_node(const knot_node_t *n);
 int knot_nsec_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
                            knot_changeset_t *changeset);
 
-/*!
- * \brief Fixes NSEC chain after DDNS/reload
- *
- * \param sorted_changes  Sorted changes created by changeset sign function.
- * \param fix_data        Chain fix data.
- *
- * \return KNOT_E*
- */
-int knot_nsec_fix_chain(hattrie_t *sorted_changes,
-                        chain_fix_data_t *fix_data);
-
-
 #endif // _KNOT_DNSSEC_NSEC_CHAIN_FIX_H_
diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c
index fbe33e90a324ff16c74214ec1219aa61da09cbc4..36b9da583299e26a520d3a24904ea3fd86e839c9 100644
--- a/src/knot/dnssec/nsec3-chain.c
+++ b/src/knot/dnssec/nsec3-chain.c
@@ -78,152 +78,6 @@ static bool are_nsec3_nodes_equal(const knot_node_t *a, const knot_node_t *b)
 	return knot_rrset_equal(a_rrset, b_rrset, KNOT_RRSET_COMPARE_WHOLE);
 }
 
-/* - Chain fix data helpers ------------------------------------------------- */
-
-/*!
- * \brief Creates knot_dname_t * from 'next hashed' NSEC3 RR field.
- *
- * \param rr         NSEC3 RRSet.
- * \param zone_apex  Zone apex dname.
- *
- * \return Created dname if successful, NULL otherwise.
- */
-static knot_dname_t *next_dname_from_nsec3_rrset(const knot_rrset_t *rr,
-                                                 const knot_dname_t *zone_apex)
-{
-	int apex_size = knot_dname_size(zone_apex);
-	if (apex_size < 0) {
-		return NULL;
-	}
-	uint8_t *next_hashed = NULL;
-	uint8_t hashed_size = 0;
-	knot_rdata_nsec3_next_hashed(rr, 0, &next_hashed, &hashed_size);
-	uint8_t *encoded = NULL;
-	int32_t encoded_size = base32hex_encode_alloc(next_hashed, hashed_size,
-	                                              &encoded);
-	if (encoded_size < 0) {
-		return NULL;
-	}
-
-	uint8_t catted_hash[encoded_size + apex_size];
-	*catted_hash = encoded_size;
-	memcpy(catted_hash + 1, encoded, encoded_size);
-	free(encoded);
-	memcpy(catted_hash + 1 + encoded_size,
-	       zone_apex, apex_size);
-	knot_dname_t *next_dname = knot_dname_copy(catted_hash);
-	if (next_dname == NULL) {
-		return NULL;
-	}
-	knot_dname_to_lower(next_dname);
-	return next_dname;
-}
-
-/*!
- * \brief Updates 'chain_start' field in 'chain_fix_data_t'.
- *
- * \param data  Data to be updated.
- * \param d     DNAME to be set.
- */
-static void update_chain_start(chain_fix_data_t *data, const knot_dname_t *d)
-{
-	assert(data && d);
-	data->chain_start = d;
-}
-
-/*!
- * \brief Updates last used node and DNAME.
- *
- * \param data  Data to be updated.
- * \param d     DNAME to be set.
- * \param n     Node to be set.
- */
-static void update_last_used(chain_fix_data_t *data, const knot_dname_t *d,
-                             const knot_node_t *n)
-{
-	assert(data && d);
-	data->last_used_dname = d;
-	data->last_used_node = n;
-}
-
-/*!
- * \brief Updates next dname with 'next_hashed' from d's NSEC3 RR.
- *
- * \param fix_data  Data to be updated.
- * \param d         DNAME to search for.
- */
-static void update_next_nsec3_dname(chain_fix_data_t *fix_data,
-                                    const knot_dname_t *d)
-
-{
-	knot_dname_free(&fix_data->next_dname);
-	if (d == NULL) {
-		fix_data->next_dname = NULL;
-	} else {
-		const knot_node_t *nsec3_node =
-			knot_zone_contents_find_nsec3_node(fix_data->zone, d);
-		assert(nsec3_node);
-		const knot_rrset_t *nsec3_rrset = knot_node_rrset(nsec3_node,
-		                                                  KNOT_RRTYPE_NSEC3);
-		assert(nsec3_rrset);
-		fix_data->next_dname =
-			next_dname_from_nsec3_rrset(nsec3_rrset,
-		                                    fix_data->zone->apex->owner);
-	}
-}
-
-/* - Misc. helpers ---------------------------------------------------------- */
-
-/*!
- * \brief Helper function - sets variables by looking for data in the zone.
- */
-static void fetch_nodes_from_zone(const knot_zone_contents_t *z,
-                                  const knot_dname_t *a,
-                                  const knot_dname_t *b,
-                                  const knot_dname_t *a_hash,
-                                  const knot_dname_t *b_hash,
-                                  const knot_node_t **a_node,
-                                  const knot_node_t **b_node,
-                                  const knot_node_t **a_nsec3_node,
-                                  const knot_node_t **b_nsec3_node)
-{
-	*a_node = knot_zone_contents_find_node(z, a);
-	*b_node = knot_zone_contents_find_node(z, b);
-	*a_nsec3_node = knot_zone_contents_find_nsec3_node(z, a_hash);
-	*b_nsec3_node = knot_zone_contents_find_nsec3_node(z, b_hash);
-}
-
-/*!
- * \brief Checks whether NSEC3 covered was not changed and is now non-auth.
- *
- * \param z               Zone to be searched.
- * \param d_hashed        Hash to look for.
- * \param sorted_changes  DDNS/reload changes.
- *
- * \return True if this node can be used, false otherwise.
- */
-static bool covered_node_usable(const knot_zone_contents_t *z,
-                                const knot_dname_t *d_hashed,
-                                const hattrie_t *sorted_changes)
-{
-	uint8_t lf[KNOT_DNAME_MAXLEN];
-	knot_dname_lf(lf, d_hashed, NULL);
-	value_t *val = hattrie_tryget((hattrie_t *)sorted_changes,
-	                              (char *)lf+1, *lf);
-	if (val == NULL) {
-		return false;
-	} else {
-		signed_info_t *info = (signed_info_t *)(*val);
-		assert(knot_dname_is_equal(info->hashed_dname, d_hashed));
-		// Get normal node
-		const knot_node_t *normal_node =
-			knot_zone_contents_find_node(z, info->dname);
-		// Usable if not deleted and not non-auth
-		return normal_node != NULL &&
-		       !knot_node_is_non_auth(normal_node);
-	}
-}
-
 /*!
  * \brief Check whether at least one RR type in node should be signed,
  *        used when signing with NSEC3.
@@ -253,159 +107,6 @@ static bool node_should_be_signed_nsec3(const knot_node_t *n)
 	return false;
 }
 
-/*!
- * \brief Checks whether NSEC3 RR in zone is valid and updates it if needed.
- *
- * \param from          Start hash in NSEC3 link.
- * \param to            Destination hash in NSEC3 link.
- * \param covered_node  Node covered by 'from' hash.
- * \param out_ch        Changes go here.
- * \param zone          Changed zone.
- * \param soa_min       TTL to use for new NSEC3 RRs.
- *
- * \return KNOT_E*
- */
-static int update_nsec3(const knot_dname_t *from, const knot_dname_t *to,
-                        const knot_node_t *covered_node,
-                        knot_changeset_t *out_ch,
-                        const knot_zone_contents_t *zone, uint32_t soa_min)
-{
-	assert(from && to && out_ch && zone);
-	// Get old NSEC3 RR (there might not be any)
-	const knot_node_t *from_node = knot_zone_contents_find_nsec3_node(zone,
-	                                                                  from);
-	const knot_rrset_t *old_nsec3 = from_node ?
-	                                knot_node_rrset(from_node,
-	                                                KNOT_RRTYPE_NSEC3) : NULL;
-
-	// Create new NSEC3 - start with binary next hashed name
-	uint8_t *b32_hash = (uint8_t *)knot_dname_to_str(to);
-	assert(zone->nsec3_params.algorithm != 0);
-	size_t b32_length =
-		knot_nsec3_hash_b32_length(zone->nsec3_params.algorithm);
-	if (b32_hash == NULL) {
-		return KNOT_ENOMEM;
-	}
-	uint8_t *binary_next = NULL;
-	int32_t written = base32hex_decode_alloc(b32_hash, b32_length,
-	                                         &binary_next);
-	free(b32_hash);
-	if (written < 0) {
-		return written;
-	}
-
-	knot_rrset_t *gen_nsec3 = NULL;
-	// Create or reuse
-	if (covered_node) {
-		// Use bitmap from given node
-		bitmap_t bm = { '\0' };
-		bitmap_add_node_rrsets(&bm, covered_node);
-		if (node_should_be_signed_nsec3(covered_node)) {
-			bitmap_add_type(&bm, KNOT_RRTYPE_RRSIG);
-		}
-		// Create owner
-		knot_dname_t *owner = knot_dname_copy(from);
-		if (owner == NULL) {
-			free(binary_next);
-			return KNOT_ENOMEM;
-		}
-		// Create the RRSet
-		gen_nsec3 = create_nsec3_rrset(owner, &zone->nsec3_params,
-		                               &bm, binary_next, soa_min);
-		if (gen_nsec3 == NULL) {
-			free(binary_next);
-			knot_dname_free(&owner);
-			return KNOT_ERROR;
-		}
-	} else {
-		assert(old_nsec3);
-		// Reuse bitmap and data from old NSEC3
-		int ret = knot_rrset_deep_copy(old_nsec3, &gen_nsec3, NULL);
-		if (ret != KNOT_EOK) {
-			free(binary_next);
-			return ret;
-		}
-		uint8_t *next_hashed = NULL;
-		uint8_t next_hashed_size;
-		knot_rdata_nsec3_next_hashed(gen_nsec3, 0, &next_hashed,
-		                             &next_hashed_size);
-		assert(next_hashed);
-		if (next_hashed_size != written) {
-			// Possible algo mismatch
-			free(binary_next);
-			knot_rrset_deep_free(&gen_nsec3, 1, NULL);
-			return KNOT_ERROR;
-		}
-		memcpy(next_hashed, binary_next, next_hashed_size);
-	}
-	free(binary_next);
-
-	if (old_nsec3 && knot_rrset_equal(old_nsec3, gen_nsec3,
-	                                  KNOT_RRSET_COMPARE_WHOLE)) {
-		// Nothing to update
-		knot_rrset_deep_free(&gen_nsec3, 1, NULL);
-		return KNOT_EOK;
-	} else {
-		// Drop old
-		int ret = KNOT_EOK;
-		if (old_nsec3) {
-			ret = knot_nsec_changeset_remove(from_node, out_ch);
-			if (ret != KNOT_EOK) {
-				knot_rrset_deep_free(&gen_nsec3, 1, NULL);
-				return ret;
-			}
-		}
-
-		// Add new
-		ret = knot_changeset_add_rrset(out_ch, gen_nsec3,
-		                               KNOT_CHANGESET_ADD);
-		if (ret != KNOT_EOK) {
-			knot_rrset_deep_free(&gen_nsec3, 1, NULL);
-			return ret;
-		}
-	}
-
-	return KNOT_EOK;
-}
-
-/*!
- * \brief  Gets first NSEC3 node from zone.
- *
- * \param  z Zone to be searched.
- *
- * \return first NSEC3 node on success, NULL otherwise.
- */
-static const knot_node_t *zone_first_nsec3_node(const knot_zone_contents_t *z)
-{
-	assert(z && hattrie_weight(z->nsec3_nodes) > 0);
-	hattrie_iter_t *i = hattrie_iter_begin(z->nsec3_nodes, true);
-	if (i == NULL) {
-		return NULL;
-	}
-	knot_node_t *first_node = (knot_node_t *)*hattrie_iter_val(i);
-	assert(first_node);
-	hattrie_iter_free(i);
-	return first_node;
-}
-
-/*!
- * \brief  Gets last NSEC3 node from zone.
- *
- * \param  z Zone to be searched.
- *
- * \return last NSEC3 node on success, NULL otherwise.
- */
-static const knot_node_t *zone_last_nsec3_node(const knot_zone_contents_t *z)
-{
-	// Get first node
-	const knot_node_t *first_node = zone_first_nsec3_node(z);
-	if (first_node == NULL) {
-		return NULL;
-	}
-	// Get node previous to first = last node
-	return knot_zone_contents_find_previous_nsec3(z, first_node->owner);
-}
-
 /* - RRSIGs handling for NSEC3 ---------------------------------------------- */
 
 /*!
@@ -777,612 +478,6 @@ static int create_nsec3_nodes(const knot_zone_contents_t *zone, uint32_t ttl,
 	return result;
 }
 
-/* - NSEC3 chain fix -------------------------------------------------------- */
-
-/* - Nonterminal handling --------------------------------------------------- */
-
-/*!
- * \brief Cuts DNAME and looks for all the labels in the zone.
- *
- * \param dname  DNAME to be cut.
- * \param zone   Zone to be searched.
- * \param t      Trie that contains empty non-terminals.
- *
- * \return KNOT_E*
- */
-static int walk_dname_and_store_empty_nonterminals(const knot_dname_t *dname,
-                                                   const knot_zone_contents_t *zone,
-                                                   hattrie_t *t)
-{
-	assert(dname);
-	assert(zone);
-	assert(t);
-
-	if (knot_dname_size(dname) == 1) {
-		// Root dname
-		assert(*dname == '\0');
-		return KNOT_EOK;
-	}
-	if (knot_dname_is_equal(dname, zone->apex->owner)) {
-		// Apex
-		return KNOT_EOK;
-	}
-
-	// Start after the first cut
-	const knot_dname_t *cut = knot_wire_next_label(dname, NULL);
-	while (*cut != '\0' && !knot_dname_is_equal(cut, zone->apex->owner)) {
-		// Search for name in the zone
-		const knot_node_t *n = knot_zone_contents_find_node(zone, cut);
-		if (n == NULL || n->rrset_count == 0) {
-			/*!
-			 * n == NULL:
-			 * This means that RR *removal* caused non-terminal
-			 * deletion - NSEC3 has to be dropped.
-			 *
-			 * n->rrset_count == 0:
-			 * This means that RR *addition* created new empty
-			 * non-terminal - NSEC3 has to be added.
-			 */
-			hattrie_insert_dname(t, (knot_dname_t *)cut);
-		}
-		cut = knot_wire_next_label(cut, NULL);
-	}
-	return KNOT_EOK;
-}
-/*!
- * \brief Cuts labels and looks for nodes in zone, if an empty node is found
- *        adds it into trie. There may be multiple nodes. Not all nodes
- *        have to be checked, but not doing that would bloat the code.
- *
- * \param zone
- * \param sorted_changes
- *
- * \return KNOT_E*
- */
-static int update_changes_with_empty_non_terminals(const knot_zone_contents_t *zone,
-                                                   hattrie_t *sorted_changes)
-{
-	assert(zone);
-	assert(sorted_changes);
-
-	/*!
-	 * Create trie with newly created nonterminals, as we cannot (probably)
-	 * insert to the trie in the middle of iteration.
-	 */
-	hattrie_t *nterminal_t = hattrie_create();
-	if (nterminal_t == NULL) {
-		return KNOT_ENOMEM;
-	}
-
-	// Start trie iteration
-	const bool sorted = false;
-	hattrie_iter_t *itt = hattrie_iter_begin(sorted_changes, sorted);
-	if (itt == NULL) {
-		return KNOT_ERROR;
-	}
-	for (; !hattrie_iter_finished(itt); hattrie_iter_next(itt)) {
-		signed_info_t *info = (signed_info_t *)*hattrie_iter_val(itt);
-		knot_dname_t *node_dname = info->dname;
-		assert(node_dname);
-		int ret = walk_dname_and_store_empty_nonterminals(node_dname,
-		                                                  zone,
-		                                                  nterminal_t);
-		if (ret != KNOT_EOK) {
-			hattrie_free(nterminal_t);
-			return ret;
-		}
-	}
-	hattrie_iter_free(itt);
-
-	// Reinsert updated nonterminals into trie (dname already converted)
-	itt = hattrie_iter_begin(nterminal_t, sorted);
-	if (itt == NULL) {
-		return KNOT_ERROR;
-	}
-	for (; !hattrie_iter_finished(itt); hattrie_iter_next(itt)) {
-		// Store keys from table directly to trie
-		size_t key_size = 0;
-		const char *k = hattrie_iter_key(itt, &key_size);
-		assert(k && key_size > 0);
-		// Create dummy value
-		signed_info_t *info = malloc(sizeof(signed_info_t));
-		if (info == NULL) {
-			ERR_ALLOC_FAILED;
-			hattrie_iter_free(itt);
-			hattrie_free(nterminal_t);
-			return KNOT_ENOMEM;
-		}
-		memset(info, 0, sizeof(signed_info_t));
-		info->dname =
-			knot_dname_copy((knot_dname_t *)(*hattrie_iter_val(itt)));
-		if (info->dname == NULL) {
-			hattrie_iter_free(itt);
-			hattrie_free(nterminal_t);
-			return KNOT_ENOMEM;
-		}
-		*hattrie_get(sorted_changes, k, key_size) = info;
-	}
-
-	hattrie_iter_free(itt);
-	hattrie_free(nterminal_t);
-
-	return KNOT_EOK;
-}
-
-/* - Changeset hashing ------------------------------------------------------ */
-
-/*!
- * \brief Iterates through changes made by DDNS/reload and NSEC3-hashes each name.
- *
- * \param sorted_changes  Changes to be iterated.
- * \param zone            Changed zone.
- * \param out             NSEC3 hashes are saved here with original DNAMEs.
- *
- * \return KNOT_E*
- */
-static int create_nsec3_hashes_from_trie(const hattrie_t *sorted_changes,
-                                         const knot_zone_contents_t *zone,
-                                         hattrie_t **out)
-{
-	assert(sorted_changes);
-	assert(hattrie_weight(sorted_changes) > 0);
-	*out = hattrie_create();
-	if (*out == NULL) {
-		return KNOT_ENOMEM;
-	}
-
-	const bool sort = false;
-	hattrie_iter_t *itt = hattrie_iter_begin(sorted_changes, sort);
-	if (itt == NULL) {
-		hattrie_free(*out);
-		return KNOT_ERROR;
-	}
-
-	for (; !hattrie_iter_finished(itt); hattrie_iter_next(itt)) {
-		signed_info_t *val = (signed_info_t *)(*hattrie_iter_val(itt));
-		const knot_dname_t *original_dname = val->dname;
-		knot_dname_t *nsec3_name =
-			knot_create_nsec3_owner(original_dname,
-		                                zone->apex->owner,
-		                                &zone->nsec3_params);
-		if (nsec3_name == NULL) {
-			hattrie_iter_free(itt);
-			hattrie_free(*out);
-			return KNOT_ERROR;
-		}
-		knot_dname_to_lower(nsec3_name);
-		val->hashed_dname = nsec3_name;
-
-		// Convert NSEC3 hash to sortable format
-		uint8_t lf[KNOT_DNAME_MAXLEN];
-		knot_dname_lf(lf, nsec3_name, NULL);
-		// Store into new trie
-		*hattrie_get(*out, (char *)lf+1, *lf) = val;
-	}
-	hattrie_iter_free(itt);
-	return KNOT_EOK;
-}
-
-/* - Actual chain fix ------------------------------------------------------- */
-
-/*!
- * \brief Fetches covered node for 'hash' from zone.
- *
- * \param fix_data  Chain fix data.
- * \param hash      Hash to search for.
- *
- * \return          Covered node if changed via DDNS/reload, NULL otherwise.
- */
-static const knot_node_t *fetch_covered_node(chain_fix_data_t *fix_data,
-                                             const knot_dname_t *hash)
-{
-	uint8_t lf[KNOT_DNAME_MAXLEN];
-	knot_dname_lf(lf, hash, NULL);
-	value_t *val = hattrie_tryget((hattrie_t *)fix_data->sorted_changes,
-	                              (char *)lf+1, *lf);
-	if (val == NULL) {
-		// No change, old bitmap can be reused
-		return NULL;
-	} else {
-		signed_info_t *info = (signed_info_t *)*val;
-		return knot_zone_contents_find_node(fix_data->zone,
-		                                    info->dname);
-	}
-}
-
-/*!
- * \brief Handles fixing of 'gaps' in NSEC3 chain.
- *
- * \param fix_data      Chain fix data.
- * \param a_hash        Hash of DNAME we want to connect to.
- * \param a_node        Node covered by 'a_hash' (normal node).
- * \param a_nsec3_node  NSEC3 node for 'a_hash'.
- *
- * \return KNOT_E*
- */
-static int handle_nsec3_next_dname(chain_fix_data_t *fix_data,
-                                   const knot_dname_t *a_hash,
-                                   const knot_node_t *a_node,
-                                   const knot_node_t *a_nsec3_node)
-{
-	assert(fix_data && fix_data->next_dname && a_hash && a_node);
-	int ret = KNOT_EOK;
-	if (knot_dname_is_equal(fix_data->next_dname, a_hash)) {
-		assert(a_nsec3_node);
-		// We have to take one more step in the chain
-		const knot_rrset_t *nsec3_rrset =
-			knot_node_rrset(a_nsec3_node, KNOT_RRTYPE_NSEC3);
-		assert(nsec3_rrset);
-		knot_dname_t *rr_next_dname =
-			next_dname_from_nsec3_rrset(nsec3_rrset,
-		                                    fix_data->zone->apex->owner);
-		if (rr_next_dname == NULL) {
-			return KNOT_ENOMEM;
-		}
-		const knot_node_t *next_node =
-			knot_zone_contents_find_nsec3_node(fix_data->zone,
-			                                   rr_next_dname);
-		assert(next_node);
-		knot_dname_free(&rr_next_dname);
-		update_last_used(fix_data, next_node->owner,
-		                 fetch_covered_node(fix_data, next_node->owner));
-		ret = update_nsec3(a_hash, rr_next_dname, a_node,
-		                   fix_data->out_ch,
-		                   fix_data->zone, fix_data->ttl);
-	} else {
-		// Next dname is usable
-		update_last_used(fix_data, fix_data->next_dname,
-		                 fetch_covered_node(fix_data, fix_data->next_dname));
-		ret = update_nsec3(a_hash, fix_data->next_dname,
-		                   a_node, fix_data->out_ch,
-		                   fix_data->zone, fix_data->ttl);
-		update_next_nsec3_dname(fix_data, NULL);
-		return ret == KNOT_EOK ? NSEC_NODE_RESET : ret;
-	}
-	update_next_nsec3_dname(fix_data, NULL);
-	return ret == KNOT_EOK ? NSEC_NODE_RESET : ret;
-}
-
-/*!
- * \brief Handles node that has been deleted by DDNS/reload.
- *
- * \param node      Deleted node
- * \param fix_data  Chain fix data.
- *
- * \return KNOT_E*, NSEC_NODE_SKIP
- */
-static int handle_deleted_node(const knot_node_t *node,
-                               chain_fix_data_t *fix_data)
-{
-	if (node == NULL) {
-		// This node was deleted and used to be non-auth
-		assert(knot_node_is_non_auth(node));
-		return NSEC_NODE_SKIP;
-	}
-	int ret = knot_nsec_changeset_remove(node, fix_data->out_ch);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	/*!
-	 * This node should be ignored, but we might need the next dname from
-	 * previous node.
-	 */
-	if (fix_data->next_dname == NULL) {
-		const knot_rrset_t *old_nsec3 =
-			knot_node_rrset(node, KNOT_RRTYPE_NSEC3);
-		assert(old_nsec3);
-		fix_data->next_dname =
-			next_dname_from_nsec3_rrset(old_nsec3,
-			                            fix_data->zone->apex->owner);
-		if (fix_data->next_dname == NULL) {
-			return KNOT_ENOMEM;
-		}
-	}
-
-	return NSEC_NODE_SKIP;
-}
-
-/*!
- * \brief Checks if old and new NSEC3 chains should be connected.
- *
- * \param fix_data Chain fix data.
- * \param a          Old chain end.
- * \param b          New chain start.
- * \param zone_prev  Previous node from zone for 'b'.
- *
- * \return True if chains should be connected, false if no.
- */
-static bool should_connect_to_old(chain_fix_data_t *fix_data,
-                                  const knot_dname_t *a, const knot_dname_t *b,
-                                  const knot_dname_t *zone_prev)
-{
-	return fix_data->chain_start && !fix_data->old_connected &&
-	       a && knot_dname_cmp(a, zone_prev) < 0 &&
-	       knot_dname_cmp(zone_prev, b) < 0;
-}
-
-/*!
- * \brief Connects old NSEC3 chain and new NSE3 chain.
- *
- * \param fix_data        Chain fix data.
- * \param a_hash          Old NSEC3 chain end.
- * \param b_hash          New NSEC3 chain beginning.
- * \param a_node          Node covered by 'a_hash', from changeset.
- * \param zone_prev_node  Nobe covered by 'a_hash', from zone.
- *
- * \return KNOT_E*
- */
-static int connect_to_old_start(chain_fix_data_t *fix_data,
-                                const knot_dname_t *a_hash,
-                                const knot_dname_t *b_hash,
-                                const knot_node_t *a_node,
-                                const knot_node_t *zone_prev_node)
-{
-	fix_data->old_connected = true;
-	assert(fix_data && a_hash && b_hash && a_node && zone_prev_node);
-	int ret = update_nsec3(a_hash, zone_prev_node->owner,
-	                       a_node, fix_data->out_ch, fix_data->zone,
-	                       fix_data->ttl);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
-	update_last_used(fix_data, b_hash,
-	                 fetch_covered_node(fix_data, b_hash));
-	return update_nsec3(zone_prev_node->owner, b_hash,
-	                    fetch_covered_node(fix_data, zone_prev_node->owner),
-	                    fix_data->out_ch, fix_data->zone, fix_data->ttl);
-}
-
-/*!
- * \brief Decides whether to use previous hash from zone or changeset.
- *
- * \param a_hash     Previous hash from changeset.
- * \param b_hash     Hash we want to connect to.
- * \param zone_prev  Previous hash from zone.
- *
- * \return True if previous dname from changeset should be used, false otherwise.
- */
-static bool use_prev_from_changeset(const knot_dname_t *a_hash,
-                                    const knot_dname_t *b_hash,
-                                    const knot_dname_t *zone_prev)
-{
-	if (a_hash) {
-		// Direct hit from changeset, or fits between zone and changeset gap
-		bool name_eq_closer = knot_dname_cmp(a_hash,
-		                                     zone_prev) >= 0;
-		// Previous node is no longer valid - new chain start was set
-		bool part_of_new_start = knot_dname_cmp(a_hash,
-		                                        zone_prev) < 0 &&
-		                         knot_dname_cmp(b_hash,
-		                                        zone_prev) <= 0;
-		return name_eq_closer || part_of_new_start;
-	} else {
-		return false;
-	}
-}
-
-/*!
- * \brief Finds previous usable NSEC3 node in zone, checks if node node not
- *        deleted in changes.
- * \param z               Zone to be searched.
- * \param d_hashed        Hash to search for.
- * \param sorted_changes  DDNS/reload changes.
- *
- * \return Previous NSEC3 node for 'd_hashed'.
- */
-static const knot_node_t *find_prev_nsec3_node(const knot_zone_contents_t *z,
-                                               const knot_dname_t *d_hashed,
-                                               const hattrie_t *sorted_changes)
-{
-	// Find previous node for the node
-	const knot_node_t *prev_nsec3_node =
-		knot_zone_contents_find_previous_nsec3(z, d_hashed);
-	assert(prev_nsec3_node);
-	bool prev_nsec3_found = !covered_node_usable(z, prev_nsec3_node->owner,
-	                                             sorted_changes);
-	while (!prev_nsec3_found) {
-		prev_nsec3_node =
-			knot_zone_contents_find_previous_nsec3(z,
-			                                       prev_nsec3_node->owner);
-		assert(prev_nsec3_node);
-		// Either the node is usable, or there's nothing more to find
-		prev_nsec3_found = covered_node_usable(z,
-		                                       prev_nsec3_node->owner,
-		                                       sorted_changes) ||
-		                   knot_dname_is_equal(prev_nsec3_node->owner,
-		                                       d_hashed);
-	}
-	return prev_nsec3_node;
-}
-
-/*!
- * \brief Fixes one link between 'a' and 'b', or rather between their hashes.
- *        'a_hash' is always < 'b_hash'. Called only via iteration function.
- *
- * \param a         Normal DNAME (changed in the update/reload)
- * \param a_hash    NSEC3 hash of 'a'.
- * \param b         Normal DNAME (changed in the update/reload)
- * \param b_hash    NSEC3 hash of 'b'.
- * \param fix_data  Fix data.
- *
- * \return KNOT_EOK if okay, KNOT_E* if something went wrong,
- *         NSEC_NODE_RESET, NSEC_NODE_SKIP if special handling is needed by the
- *         iteration funtion.
- */
-static int fix_nsec3_chain(knot_dname_t *a, knot_dname_t *a_hash,
-                           knot_dname_t *b, knot_dname_t *b_hash,
-                           chain_fix_data_t *fix_data)
-{
-	assert(b && b_hash);
-	assert((!a && !a_hash) || (a && a_hash));
-	assert(fix_data);
-	// Get nodes from zone
-	const knot_node_t *a_node, *b_node, *a_nsec3_node, *b_nsec3_node;
-	fetch_nodes_from_zone(fix_data->zone, a, b, a_hash, b_hash, &a_node,
-	                      &b_node, &a_nsec3_node, &b_nsec3_node);
-	// Find previous node in zone ('proper' node might not be in the zone yet)
-	const knot_node_t *prev_nsec3_node =
-		find_prev_nsec3_node(fix_data->zone, b_hash,
-		                     fix_data->sorted_changes);
-	if (prev_nsec3_node == NULL) {
-		// Should not happen, zone would have to have no NSEC3 chain
-		return KNOT_ERROR;
-	}
-
-	// Handle possible node removal
-	bool node_deleted = b_node == NULL;
-	if (node_deleted) {
-		// The deleted node might have been authoritative, but not anymore
-		if (fix_data->last_used_dname == NULL) {
-			update_last_used(fix_data, prev_nsec3_node->owner,
-			                 fetch_covered_node(fix_data,
-			                                    prev_nsec3_node->owner));
-		}
-		return handle_deleted_node(b_nsec3_node, fix_data);
-	}
-	if (knot_node_is_non_auth(b_node)) {
-		// Nothing to fix in this node
-		return NSEC_NODE_SKIP;
-	}
-
-	// Find out whether to use a node from changeset or from zone
-	bool use_prev_from_chgs = use_prev_from_changeset(a_hash, b_hash,
-	                                                  prev_nsec3_node->owner);
-	if (use_prev_from_chgs) {
-		// No valid data for the previous node, create the forward NSEC3
-		update_last_used(fix_data, b_hash, b_node);
-		return update_nsec3(a_hash, b_hash, a_node, fix_data->out_ch,
-		                    fix_data->zone, fix_data->ttl);
-	}
-	if (should_connect_to_old(fix_data,
-	                          a_hash, b_hash, prev_nsec3_node->owner)) {
-		// Connect old start with new start
-		return connect_to_old_start(fix_data, a_hash, b_hash, a_node,
-		                            prev_nsec3_node);
-	}
-
-	// Use either next_dname or data from zone
-	bool new_chain_start =
-		knot_dname_cmp(prev_nsec3_node->owner, b_hash) > 0 &&
-		!(zone_first_nsec3_node(fix_data->zone) == b_nsec3_node);
-	if (new_chain_start) {
-		assert(a == NULL); // This has to be the first change
-		// New chain started by this change
-		update_last_used(fix_data, b_hash, b_node);
-		update_chain_start(fix_data, b_hash);
-		return KNOT_EOK;
-	} else if (fix_data->next_dname) {
-		return handle_nsec3_next_dname(fix_data, a_hash,
-		                               a_node, a_nsec3_node);
-	} else {
-		// Previous node was not changed in DDNS, NSEC3 has to be present
-		assert(knot_node_rrset(prev_nsec3_node, KNOT_RRTYPE_NSEC3));
-		update_next_nsec3_dname(fix_data, prev_nsec3_node->owner);
-		update_last_used(fix_data, b_hash, b_node);
-		return update_nsec3(prev_nsec3_node->owner, b_hash,
-		                    fetch_covered_node(fix_data, prev_nsec3_node->owner),
-		                    fix_data->out_ch, fix_data->zone,
-		                    fix_data->ttl);
-	}
-
-	return KNOT_EOK;
-}
-
-/*!
- * \brief Finalizes NSEC3 chain.
- *
- * \param fix_data Chain fix data.
- *
- * \return KNOT_E*
- */
-static int chain_finalize_nsec3(chain_fix_data_t *fix_data)
-{
-	assert(fix_data);
-	if (fix_data->next_dname == NULL && fix_data->chain_start == NULL) {
-		// Nothing to fix
-		return KNOT_EOK;
-	}
-	const knot_dname_t *from = fix_data->last_used_dname;
-	assert(from);
-	const knot_node_t *from_node = fix_data->last_used_node;
-	const knot_dname_t *to = NULL;
-	if (fix_data->chain_start) {
-		/*!
-		 * New chain start has to be closed - get last dname
-		 * in the chain from zone or changeset.
-		 */
-		const knot_node_t *last_node =
-			zone_last_nsec3_node(fix_data->zone);
-		if (last_node == NULL) {
-			return KNOT_ENOMEM;
-		}
-		if (!fix_data->old_connected) {
-			/*!
-			 * New chain was started, but not connected to
-			 * the old one.
-			 */
-			const knot_node_t *first_nsec3 =
-				zone_first_nsec3_node(fix_data->zone);
-			if (first_nsec3 == NULL) {
-				return KNOT_ENOMEM;
-			}
-
-			int ret = update_nsec3(fix_data->last_used_dname,
-			                       first_nsec3->owner,
-			                       fix_data->last_used_node,
-			                       fix_data->out_ch,
-			                       fix_data->zone, fix_data->ttl);
-			if (ret != KNOT_EOK) {
-				return ret;
-			}
-		}
-		// Close the chain
-		to = fix_data->chain_start;
-		if (knot_dname_cmp(last_node->owner,
-		                   fix_data->last_used_dname) > 0) {
-			// Use last zone node to close the chain
-			from = last_node->owner;
-			from_node = NULL; // Was not changed
-		}
-	} else if (knot_dname_is_equal(from,
-	                               fix_data->zone->apex->nsec3_node->owner)) {
-		// Special case where all nodes but the apex are deleted
-		to = fix_data->last_used_dname;
-	} else if (knot_dname_is_equal(from, fix_data->next_dname)) {
-		// We do not want to point it to itself, extract next
-		const knot_node_t *nsec3_node =
-			knot_zone_contents_find_nsec3_node(fix_data->zone,
-			                                   from);
-		assert(nsec3_node);
-		const knot_rrset_t *nsec3_rrset =
-			knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
-		assert(nsec3_rrset);
-		knot_dname_free(&fix_data->next_dname);
-		knot_dname_t *next =
-			next_dname_from_nsec3_rrset(nsec3_rrset,
-			                            fix_data->zone->apex->owner);
-		if (next == NULL) {
-			return KNOT_ENOMEM;
-		}
-		// We have to call update here, since different name should be freed
-		int ret = update_nsec3(from, next, fix_data->last_used_node,
-		                       fix_data->out_ch, fix_data->zone,
-		                       fix_data->ttl);
-		knot_dname_free(&next);
-		return ret;
-	} else {
-		// Normal case
-		to = fix_data->next_dname;
-	}
-	assert(to);
-	int ret = update_nsec3(from, to, from_node,
-	                       fix_data->out_ch, fix_data->zone, fix_data->ttl);
-	knot_dname_free(&fix_data->next_dname);
-	return ret;
-}
-
 /*!
  * \brief Checks if NSEC3 should be generated for this node.
  *
@@ -1542,31 +637,3 @@ int knot_nsec3_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
 	return result;
 }
 
-/*!
- * \brief Fixes NSEC3 chain after DDNS/reload.
- */
-int knot_nsec3_fix_chain(hattrie_t *sorted_changes, chain_fix_data_t *fix_data)
-{
-	// Empty non-terminals are not in the changes, update
-	int ret = update_changes_with_empty_non_terminals(fix_data->zone,
-	                                                  sorted_changes);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-	// Create and sort NSEC3 hashes
-	hattrie_t *nsec3_names = NULL;
-	ret = create_nsec3_hashes_from_trie(sorted_changes,
-	                                    fix_data->zone,
-	                                    &nsec3_names);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-	hattrie_build_index(nsec3_names);
-	fix_data->sorted_changes = nsec3_names;
-
-	// Fix NSEC3 chain
-	ret = knot_nsec_chain_iterate_fix(nsec3_names, fix_nsec3_chain,
-	                                  chain_finalize_nsec3, fix_data);
-	hattrie_free(nsec3_names);
-	return ret;
-}
diff --git a/src/knot/dnssec/nsec3-chain.h b/src/knot/dnssec/nsec3-chain.h
index d2f1f072ed15cef5972d83fde759b16f4d0b8440..9437c29704adf9ce94d2f1fda601a610f94d4e68 100644
--- a/src/knot/dnssec/nsec3-chain.h
+++ b/src/knot/dnssec/nsec3-chain.h
@@ -16,10 +16,9 @@
 /*!
  * \file nsec3-chain-fix.h
  *
- * \author Jan Kadlec <jan.kadlec@nic.cz> (chain fix)
  * \author Jan Vcelak <jan.vcelak@nic.cz> (chain creation)
  *
- * \brief NSEC3 chain fix and creation.
+ * \brief NSEC3 chain creation.
  *
  * \addtogroup dnssec
  * @{
@@ -43,14 +42,4 @@
 int knot_nsec3_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
                             knot_changeset_t *changeset);
 
-/*!
- * \brief Fixes NSEC3 chain after DDNS/reload.
- *
- * \param sorted_changes  Sorted changes created by changeset sign function.
- * \param fix_data        Chain fix data.
- *
- * \return KNOT_E*
- */
-int knot_nsec3_fix_chain(hattrie_t *sorted_changes, chain_fix_data_t *fix_data);
-
 #endif // _KNOT_DNSSEC_NSEC3_CHAIN_FIX_H_
diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c
index 2c5ce829a6a95e5b0261355c94ffb8cc71cc05ae..00872d41af56c7f1fb6f8bec543b6710cd8257fe 100644
--- a/src/knot/dnssec/zone-events.c
+++ b/src/knot/dnssec/zone-events.c
@@ -182,10 +182,9 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
                                knot_changeset_t *out_ch,
                                knot_update_serial_t soa_up,
                                uint32_t *refresh_at,
-                               uint32_t new_serial,
-                               hattrie_t **sorted_changes)
+                               uint32_t new_serial)
 {
-	if (!refresh_at || !sorted_changes) {
+	if (!refresh_at) {
 		return KNOT_EINVAL;
 	}
 
@@ -210,7 +209,7 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
 	}
 
 	// Sign added and removed RRSets in changeset
-	ret = knot_zone_sign_changeset(zone, in_ch, out_ch, sorted_changes,
+	ret = knot_zone_sign_changeset(zone, in_ch, out_ch,
 	                               &zone_keys, &policy);
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s Failed to sign changeset (%s)\n", msgpref,
@@ -220,13 +219,10 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
 		return ret;
 	}
 
-	assert(sorted_changes);
-	// Fix NSEC(3) chain
-	ret = knot_zone_fix_nsec_chain(zone,
-	                               *sorted_changes, out_ch,
-	                               &zone_keys, &policy);
+	// Create NSEC(3) chain
+	ret = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy);
 	if (ret != KNOT_EOK) {
-		log_zone_error("%s Failed to fix NSEC(3) chain (%s)\n",
+		log_zone_error("%s Failed to create NSEC(3) chain (%s)\n",
 		               msgpref, knot_strerror(ret));
 		knot_free_zone_keys(&zone_keys);
 		free(msgpref);
diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h
index cb94526f37cacb4e6f3459e64fbf299b7b034853..ab6f2fd5eca8dc94676fab3c218f312448ba4b11 100644
--- a/src/knot/dnssec/zone-events.h
+++ b/src/knot/dnssec/zone-events.h
@@ -75,7 +75,6 @@ int knot_dnssec_zone_sign_force(knot_zone_contents_t *zone, conf_zone_t *zone_co
  * \param soa_up          SOA serial update policy.
  * \param refresh_at      Signature refresh time of the new signatures.
  * \param new_serial      New SOA serial.
- * \param sorted_changes  Info about made changes, used for partial adjustment.
  *
  * \return Error code, KNOT_EOK if successful.
  */
@@ -84,8 +83,7 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
                                const knot_changeset_t *in_ch,
                                knot_changeset_t *out_ch,
                                knot_update_serial_t soa_up,
-                               uint32_t *refresh_at, uint32_t new_serial,
-                               hattrie_t **sorted_changes);
+                               uint32_t *refresh_at, uint32_t new_serial);
 
 #endif // _KNOT_DNSSEC_ZONE_EVENTS_H_
 /*! @} */
diff --git a/src/knot/dnssec/zone-nsec.c b/src/knot/dnssec/zone-nsec.c
index 67121f9296170d9c598df08450f85d2167865e4f..9e1ef9f51ebed924be2798d27c7d2b50aa33e667 100644
--- a/src/knot/dnssec/zone-nsec.c
+++ b/src/knot/dnssec/zone-nsec.c
@@ -287,44 +287,3 @@ int knot_zone_create_nsec_chain(const knot_zone_contents_t *zone,
 	return knot_zone_sign_nsecs_in_changeset(zone_keys, policy, changeset);
 }
 
-/*!
- * \brief Fix NSEC or NSEC3 chain in the zone.
- */
-int knot_zone_fix_nsec_chain(const knot_zone_contents_t *zone,
-                             hattrie_t *sorted_changes,
-                             knot_changeset_t *out_ch,
-                             const knot_zone_keys_t *zone_keys,
-                             const knot_dnssec_policy_t *policy)
-{
-	if (zone == NULL || sorted_changes == NULL || zone_keys == NULL ||
-	    policy == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (hattrie_weight(sorted_changes) == 0) {
-		// no changes, no fix
-		return KNOT_EOK;
-	}
-
-	// Prepare data for chain fixing functions
-	chain_fix_data_t fix_data = { .zone = zone,
-	                              .out_ch = out_ch,
-	                              .next_dname = NULL,
-	                              .chain_start = NULL,
-	                              .old_connected = false,
-	                              .last_used_dname = NULL,
-	                              .last_used_node = NULL};
-	get_zone_soa_min_ttl(zone, &fix_data.ttl);
-	int ret = KNOT_EOK;
-	if (knot_is_nsec3_enabled(zone)) {
-		ret = knot_nsec3_fix_chain(sorted_changes, &fix_data);
-	} else {
-
-		// Fix NSEC chain
-		ret = knot_nsec_fix_chain(sorted_changes, &fix_data);
-	}
-
-	dbg_dnssec_verb("NSEC(3) chain fixed (%s)\n", knot_strerror(ret));
-
-	return ret;
-}
diff --git a/src/knot/dnssec/zone-nsec.h b/src/knot/dnssec/zone-nsec.h
index aa7f53956363fd9bc8347756201a2f3f0992f700..36bb35660694c1738eeacb9ffa8441cd2fca7701 100644
--- a/src/knot/dnssec/zone-nsec.h
+++ b/src/knot/dnssec/zone-nsec.h
@@ -85,25 +85,6 @@ int knot_zone_create_nsec_chain(const knot_zone_contents_t *zone,
                                 const knot_zone_keys_t *zone_keys,
                                 const knot_dnssec_policy_t *policy);
 
-/*!
- * \brief Fix NSEC or NSEC3 chain in the zone.
- *
- * \param zone            Zone for which the NSEC(3) chain will be created.
- * \param sorted_changes  Sorted changes created by 'sign_changeset' function.
- *                        This param is updated with normal node -> NSEC3 node
- *                        links, to be used later when adjusting zone.
- * \param out_ch          Changeset into which the changes will be added.
- * \param zone_keys       Zone keys used for NSEC(3) creation.
- * \param policy          DNSSEC signing policy.
- *
- * \return Error code, KNOT_EOK if successful.
- */
-int knot_zone_fix_nsec_chain(const knot_zone_contents_t *zone,
-                             hattrie_t *sorted_changes,
-                             knot_changeset_t *out_ch,
-                             const knot_zone_keys_t *zone_keys,
-                             const knot_dnssec_policy_t *policy);
-
 #endif // _KNOT_DNSSEC_ZONE_NSEC_H_
 
 /*! @} */
diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c
index d6fde4384baea81422c9104fc0d064257ed846e1..7f87040b76e472035a700db8f0e99409cfd9c0fe 100644
--- a/src/knot/dnssec/zone-sign.c
+++ b/src/knot/dnssec/zone-sign.c
@@ -1204,6 +1204,12 @@ static int sign_changeset_wrap(knot_rrset_t *chg_rrset, void *data)
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Frees info node about update signing.
+ *
+ * \param val  Node to free.
+ * \param d    Unused.
+ */
 static int free_helper_trie_node(value_t *val, void *d)
 {
 	UNUSED(d);
@@ -1218,6 +1224,18 @@ static int free_helper_trie_node(value_t *val, void *d)
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Clears trie with info about update signing.
+ *
+ * \param t  Trie to clear.
+ */
+static void knot_zone_clear_sorted_changes(hattrie_t *t)
+{
+	if (t) {
+		hattrie_apply_rev(t, free_helper_trie_node, NULL);
+	}
+}
+
 /*- public API ---------------------------------------------------------------*/
 
 /*!
@@ -1369,12 +1387,10 @@ int knot_zone_sign_update_soa(const knot_rrset_t *soa,
 int knot_zone_sign_changeset(const knot_zone_contents_t *zone,
                              const knot_changeset_t *in_ch,
                              knot_changeset_t *out_ch,
-                             hattrie_t **sorted_changes,
                              const knot_zone_keys_t *zone_keys,
                              const knot_dnssec_policy_t *policy)
 {
-	if (zone == NULL || in_ch == NULL || out_ch == NULL ||
-	    sorted_changes == NULL) {
+	if (zone == NULL || in_ch == NULL || out_ch == NULL) {
 		return KNOT_EINVAL;
 	}
 
@@ -1398,13 +1414,11 @@ int knot_zone_sign_changeset(const knot_zone_contents_t *zone,
 		ret = knot_changeset_apply((knot_changeset_t *)in_ch,
 		                           KNOT_CHANGESET_REMOVE,
 		                           sign_changeset_wrap, &args);
-	} else {
-		knot_zone_clear_sorted_changes(args.signed_tree);
-		hattrie_free(args.signed_tree);
-		args.signed_tree = NULL;
 	}
 
-	*sorted_changes = args.signed_tree;
+	knot_zone_clear_sorted_changes(args.signed_tree);
+	hattrie_free(args.signed_tree);
+
 	return ret;
 }
 
@@ -1492,9 +1506,3 @@ int knot_zone_sign_rr_should_be_signed(const knot_node_t *node,
 	return KNOT_EOK;
 }
 
-void knot_zone_clear_sorted_changes(hattrie_t *t)
-{
-	if (t) {
-		hattrie_apply_rev(t, free_helper_trie_node, NULL);
-	}
-}
diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h
index 752e63f8743d617dba749f7e86a077a511c3d7b1..95bb79060299ab1d3ec92ab7485aed007d08bffe 100644
--- a/src/knot/dnssec/zone-sign.h
+++ b/src/knot/dnssec/zone-sign.h
@@ -99,7 +99,6 @@ bool knot_zone_sign_soa_expired(const knot_zone_contents_t *zone,
  * \param zone New zone contents.
  * \param in_ch Changeset created bvy DDNS or zone-diff
  * \param out_ch New records will be added to this changeset.
- * \param sorted_changes Sorted representation of changes.
  * \param zone_keys Keys to use for signing.
  * \param policy DNSSEC signing policy.
  *
@@ -108,7 +107,6 @@ bool knot_zone_sign_soa_expired(const knot_zone_contents_t *zone,
 int knot_zone_sign_changeset(const knot_zone_contents_t *zone,
                              const knot_changeset_t *in_ch,
                              knot_changeset_t *out_ch,
-                             hattrie_t **sorted_changes,
                              const knot_zone_keys_t *zone_keys,
                              const knot_dnssec_policy_t *policy);
 
@@ -141,8 +139,6 @@ int knot_zone_sign_rr_should_be_signed(const knot_node_t *node,
                                        const knot_rrset_t *rrset,
                                        hattrie_t *trie, bool *should_sign);
 
-void knot_zone_clear_sorted_changes(hattrie_t *t);
-
 #endif // _KNOT_DNSSEC_ZONE_SIGN_H_
 
 /*! @} */
diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
index 90cc685df9dab6d8514893f84742ed4adc2c1c5c..c22830777403c0784c334a4624d97ea1e35213e5 100644
--- a/src/knot/nameserver/update.c
+++ b/src/knot/nameserver/update.c
@@ -230,8 +230,7 @@ int knot_ns_process_update(const knot_pkt_t *query,
 
 	// 3) Finalize zone
 	dbg_ns_verb("Finalizing updated zone...\n");
-	ret = xfrin_finalize_updated_zone(contents_copy, false,
-	                                  NULL);
+	ret = xfrin_finalize_updated_zone(contents_copy, false);
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to finalize updated zone: %s\n",
 		       knot_strerror(ret));
@@ -348,8 +347,6 @@ static int zones_process_update_auth(zone_t *zone, knot_pkt_t *query,
 
 	// Apply changeset to zone created by DDNS processing
 
-	hattrie_t *sorted_changes = NULL;
-
 	if (zone->conf->dnssec_enable) {
 		/*!
 		 * Check if the UPDATE changed DNSKEYs. If yes, resign the whole
@@ -368,7 +365,7 @@ static int zones_process_update_auth(zone_t *zone, knot_pkt_t *query,
 			                      knot_changesets_get_last(chgsets),
 			                      sec_ch, KNOT_SOA_SERIAL_KEEP,
 			                      &refresh_at,
-			                      new_serial, &sorted_changes);
+			                      new_serial);
 		}
 
 		if (ret != KNOT_EOK) {
@@ -403,10 +400,7 @@ static int zones_process_update_auth(zone_t *zone, knot_pkt_t *query,
 		ret = xfrin_apply_changesets_dnssec_ddns(old_contents,
 		                                    new_contents,
 		                                    sec_chs,
-		                                    chgsets,
-		                                    sorted_changes);
-		knot_zone_clear_sorted_changes(sorted_changes);
-		hattrie_free(sorted_changes);
+		                                    chgsets);
 		if (ret != KNOT_EOK) {
 			log_zone_error("%s: Failed to sign incoming update (%s)"
 			               "\n", msg, knot_strerror(ret));
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index c8750f604059ddcda27254fc31d25d5a7aa148a5..b7c76f883fe093447362752d55b8afbfa33e43be 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -2027,7 +2027,7 @@ static int diff_after_load(zone_t *zone, zone_t *old_zone,
 
 		if (ret == KNOT_EOK) {
 			ret = xfrin_finalize_updated_zone(
-			                        zone->contents, true, NULL);
+			                        zone->contents, true);
 		}
 
 		if (ret != KNOT_EOK) {
@@ -2115,7 +2115,7 @@ static int store_chgsets_after_load(zone_t *old_zone, zone_t *zone,
 			                                      diff_chs);
 			if (ret == KNOT_EOK) {
 				ret = xfrin_finalize_updated_zone(
-				                    zone->contents, true, NULL);
+				                    zone->contents, true);
 			}
 		} else {
 			assert(old_zone != NULL);
diff --git a/src/knot/updates/xfr-in.c b/src/knot/updates/xfr-in.c
index 46a089ed211618a155f3da33584b4371c90209d9..c94f2719e72cb07f10e45983e504ef5aa6db9784 100644
--- a/src/knot/updates/xfr-in.c
+++ b/src/knot/updates/xfr-in.c
@@ -1506,42 +1506,6 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *z)
 
 /*----------------------------------------------------------------------------*/
 
-static int adjust_nsec3_changes(knot_zone_contents_t *contents,
-                                hattrie_t *changes)
-{
-	if (contents->nsec3_nodes == NULL) {
-		return KNOT_EOK;
-	}
-	hattrie_iter_t *itt = hattrie_iter_begin(changes, false);
-	if (itt == NULL) {
-		return KNOT_ENOMEM;
-	}
-	while (!hattrie_iter_finished(itt)) {
-		signed_info_t *val = (signed_info_t *)(*hattrie_iter_val(itt));
-		const knot_dname_t *dname = val->dname;
-		assert(dname);
-		const knot_dname_t *hash = val->hashed_dname;
-		if (hash) {
-			knot_node_t *nsec3_node =
-				knot_zone_contents_get_nsec3_node(contents, hash);
-			if (nsec3_node) {
-				knot_node_t *normal_node =
-					knot_zone_contents_get_node(contents,
-					                            dname);
-				if (normal_node) {
-					normal_node->nsec3_node = nsec3_node;
-				}
-			}
-		}
-		hattrie_iter_next(itt);
-	}
-
-	hattrie_iter_free(itt);
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
 int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
                             knot_zone_contents_t **new_contents)
 {
@@ -1603,8 +1567,7 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
 /*----------------------------------------------------------------------------*/
 
 int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
-                                bool set_nsec3_names,
-                                const hattrie_t *sorted_changes)
+                                bool set_nsec3_names)
 {
 	if (contents_copy == NULL) {
 		return KNOT_EINVAL;
@@ -1632,14 +1595,8 @@ int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
 
 	dbg_xfrin("Adjusting zone contents.\n");
 	if (set_nsec3_names) {
-		if (sorted_changes) {
-			ret = knot_zone_contents_adjust_pointers(contents_copy);
-			ret = adjust_nsec3_changes(contents_copy,
-			                           (void *)sorted_changes);
-		} else {
-			ret = knot_zone_contents_adjust_full(contents_copy,
-			                                     NULL, NULL);
-		}
+		ret = knot_zone_contents_adjust_full(contents_copy,
+		                                     NULL, NULL);
 	} else {
 		ret = knot_zone_contents_adjust_pointers(contents_copy);
 	}
@@ -1681,8 +1638,7 @@ int xfrin_apply_changesets_directly(knot_zone_contents_t *contents,
 int xfrin_apply_changesets_dnssec_ddns(knot_zone_contents_t *z_old,
                                        knot_zone_contents_t *z_new,
                                        knot_changesets_t *sec_chsets,
-                                       knot_changesets_t *chsets,
-                                       const hattrie_t *sorted_changes)
+                                       knot_changesets_t *chsets)
 {
 	if (z_old == NULL || z_new == NULL ||
 	    sec_chsets == NULL || chsets == NULL) {
@@ -1703,7 +1659,7 @@ int xfrin_apply_changesets_dnssec_ddns(knot_zone_contents_t *z_old,
 	}
 
 	const bool handle_nsec3 = true;
-	ret = xfrin_finalize_updated_zone(z_new, handle_nsec3, sorted_changes);
+	ret = xfrin_finalize_updated_zone(z_new, handle_nsec3);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to finalize updated zone: %s\n",
 		          knot_strerror(ret));
@@ -1766,7 +1722,7 @@ int xfrin_apply_changesets(zone_t *zone,
 	 */
 
 	dbg_xfrin_verb("Finalizing updated zone...\n");
-	ret = xfrin_finalize_updated_zone(contents_copy, true, NULL);
+	ret = xfrin_finalize_updated_zone(contents_copy, true);
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to finalize updated zone: %s\n",
 			  knot_strerror(ret));
diff --git a/src/knot/updates/xfr-in.h b/src/knot/updates/xfr-in.h
index df11deefbf57051a9c8eb225ae4f1b3045d08696..474d2a82d3f81869e5d012f7e926f4c2e85e0c35 100644
--- a/src/knot/updates/xfr-in.h
+++ b/src/knot/updates/xfr-in.h
@@ -159,7 +159,6 @@ int xfrin_apply_changesets(zone_t *zone,
  * \param z_new           Post DDNS/reload zone.
  * \param sec_chsets      Changes with RRSIGs/NSEC(3)s.
  * \param chsets          DDNS/reload changes, for rollback.
- * \param sorted_changes  Used for node->nsec3 node mapping.
  * \return KNOT_E*
  *
  * This function does not do shallow copy of the zone, as it is already created
@@ -169,8 +168,7 @@ int xfrin_apply_changesets(zone_t *zone,
 int xfrin_apply_changesets_dnssec_ddns(knot_zone_contents_t *z_old,
                                        knot_zone_contents_t *z_new,
                                        knot_changesets_t *sec_chsets,
-                                       knot_changesets_t *chsets,
-                                       const hattrie_t *sorted_changes);
+                                       knot_changesets_t *chsets);
 
 /*!
  * \brief Applies changesets directly to the zone, without copying it.
@@ -196,13 +194,10 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
  * \brief Sets pointers and NSEC3 nodes after signing/DDNS.
  * \param contents_copy    Contents to be updated.
  * \param set_nsec3_names  Set to true if NSEC3 hashes should be set.
- * \param sorted_changes   If this is non-NULL, it is used for normal node->NSEC3
- *                         node mapping, no hashes are calculated.
  * \return KNOT_E*
  */
 int xfrin_finalize_updated_zone(knot_zone_contents_t *contents_copy,
-                                bool set_nsec3_names,
-                                const hattrie_t *sorted_changes);
+                                bool set_nsec3_names);
 
 int xfrin_switch_zone(zone_t *zone,
                       knot_zone_contents_t *new_contents,