diff --git a/common/src/main/java/bisq/common/ExcludeForHash.java b/common/src/main/java/bisq/common/ExcludeForHash.java
new file mode 100644
index 00000000000..25834c158f4
--- /dev/null
+++ b/common/src/main/java/bisq/common/ExcludeForHash.java
@@ -0,0 +1,28 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.common;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExcludeForHash {
+}
diff --git a/common/src/main/java/bisq/common/ExcludeForHashAwareProto.java b/common/src/main/java/bisq/common/ExcludeForHashAwareProto.java
new file mode 100644
index 00000000000..18977fb3b9f
--- /dev/null
+++ b/common/src/main/java/bisq/common/ExcludeForHashAwareProto.java
@@ -0,0 +1,119 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.common;
+
+
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.Message;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import java.lang.reflect.Field;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Borrowed from Bisq2, but as we want to reduce risk of breaking things, specially for the DAO we pack
+ * the support for ExcludeForHash annotations into a dedicated interface.
+ * Classes which use ExcludeForHash need to implement that interface.
+ */
+public interface ExcludeForHashAwareProto extends Proto {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Override Proto methods
+ ////////////////////////////////////////////////////////////////////////////////
+
+ default Message toProtoMessage() {
+ return completeProto();
+ }
+
+ default byte[] serialize() {
+ return resolveProto(false).toByteArray();
+ }
+
+ default byte[] serializeForHash() {
+ return resolveProto(true).toByteArray();
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // API
+ ////////////////////////////////////////////////////////////////////////////////
+
+ Message.Builder getBuilder(boolean serializeForHash);
+
+ Message toProto(boolean serializeForHash);
+
+ default Message completeProto() {
+ return toProto(false);
+ }
+
+ default T resolveProto(boolean serializeForHash) {
+ //noinspection unchecked
+ return (T) resolveBuilder(getBuilder(serializeForHash), serializeForHash).build();
+ }
+
+ default B resolveBuilder(B builder, boolean serializeForHash) {
+ return serializeForHash ? clearAnnotatedFields(builder) : builder;
+ }
+
+ default int getSerializedSize() {
+ return resolveProto(false).getSerializedSize();
+ }
+
+ default void writeDelimitedTo(OutputStream outputStream) throws IOException {
+ completeProto().writeDelimitedTo(outputStream);
+ }
+
+ default Set getExcludedFields() {
+ return Arrays.stream(getClass().getDeclaredFields())
+ .peek(field -> field.setAccessible(true))
+ .filter(field -> field.isAnnotationPresent(ExcludeForHash.class))
+ .map(Field::getName)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Requires that the name of the java fields is the same as the name of the proto definition.
+ *
+ * @param builder The builder we transform by clearing the ExcludeForHash annotated fields.
+ * @return Builder with the fields annotated with ExcludeForHash cleared.
+ */
+ default B clearAnnotatedFields(B builder) {
+ Set excludedFields = getExcludedFields();
+ if (!excludedFields.isEmpty()) {
+ getLogger().error("Clear fields in builder annotated with @ExcludeForHash: {}", excludedFields);
+ }
+ for (Descriptors.FieldDescriptor fieldDesc : builder.getAllFields().keySet()) {
+ if (excludedFields.contains(fieldDesc.getName())) {
+ builder.clearField(fieldDesc);
+ }
+ }
+ return builder;
+ }
+
+ private Logger getLogger() {
+ return LoggerFactory.getLogger(getClass().getSimpleName());
+ }
+}
diff --git a/common/src/main/java/bisq/common/Proto.java b/common/src/main/java/bisq/common/Proto.java
index 1d5bf8dc590..cdc37073eae 100644
--- a/common/src/main/java/bisq/common/Proto.java
+++ b/common/src/main/java/bisq/common/Proto.java
@@ -29,6 +29,8 @@ default byte[] serialize() {
return toProtoMessage().toByteArray();
}
+ // If the class implements ExcludedFieldsProto this method will be overwritten so that
+ // fields annotated with ExcludeForHash will be excluded.
default byte[] serializeForHash() {
return serialize();
}