mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-15 11:45:49 +00:00
Hash the current state when creating a patch
This is included in the patch metadata and then validated every time it is rebuilt.
This commit is contained in:
parent
9cd579d3db
commit
a418fb18b6
@ -24,7 +24,7 @@ public class ArbitraryDataCombiner {
|
|||||||
private Path pathAfter;
|
private Path pathAfter;
|
||||||
private byte[] signatureBefore;
|
private byte[] signatureBefore;
|
||||||
private Path finalPath;
|
private Path finalPath;
|
||||||
ArbitraryDataMetadataPatch metadata;
|
private ArbitraryDataMetadataPatch metadata;
|
||||||
|
|
||||||
public ArbitraryDataCombiner(Path pathBefore, Path pathAfter, byte[] signatureBefore) {
|
public ArbitraryDataCombiner(Path pathBefore, Path pathAfter, byte[] signatureBefore) {
|
||||||
this.pathBefore = pathBefore;
|
this.pathBefore = pathBefore;
|
||||||
@ -39,6 +39,7 @@ public class ArbitraryDataCombiner {
|
|||||||
this.validatePreviousSignature();
|
this.validatePreviousSignature();
|
||||||
this.validatePreviousHash();
|
this.validatePreviousHash();
|
||||||
this.process();
|
this.process();
|
||||||
|
this.validateCurrentHash();
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
this.postExecute();
|
this.postExecute();
|
||||||
@ -132,6 +133,22 @@ public class ArbitraryDataCombiner {
|
|||||||
this.finalPath = merge.getMergePath();
|
this.finalPath = merge.getMergePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateCurrentHash() throws IOException {
|
||||||
|
byte[] currentHash = this.metadata.getCurrentHash();
|
||||||
|
if (currentHash == null) {
|
||||||
|
throw new IllegalStateException("Unable to extract current hash from patch metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.finalPath);
|
||||||
|
digest.compute();
|
||||||
|
boolean valid = digest.isHashValid(currentHash);
|
||||||
|
if (!valid) {
|
||||||
|
String currentHash58 = Base58.encode(currentHash);
|
||||||
|
throw new InvalidObjectException(String.format("Current state hash mismatch. " +
|
||||||
|
"Patch curHash: %s, actual: %s", currentHash58, digest.getHash58()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Path getFinalPath() {
|
public Path getFinalPath() {
|
||||||
return this.finalPath;
|
return this.finalPath;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ public class ArbitraryDataDiff {
|
|||||||
private Path pathAfter;
|
private Path pathAfter;
|
||||||
private byte[] previousSignature;
|
private byte[] previousSignature;
|
||||||
private byte[] previousHash;
|
private byte[] previousHash;
|
||||||
|
private byte[] currentHash;
|
||||||
private Path diffPath;
|
private Path diffPath;
|
||||||
private String identifier;
|
private String identifier;
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ public class ArbitraryDataDiff {
|
|||||||
this.findAddedOrModifiedFiles();
|
this.findAddedOrModifiedFiles();
|
||||||
this.findRemovedFiles();
|
this.findRemovedFiles();
|
||||||
this.validate();
|
this.validate();
|
||||||
|
this.hashCurrentState();
|
||||||
this.writeMetadata();
|
this.writeMetadata();
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
@ -272,6 +274,12 @@ public class ArbitraryDataDiff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hashCurrentState() throws IOException {
|
||||||
|
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathAfter);
|
||||||
|
digest.compute();
|
||||||
|
this.currentHash = digest.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
private void writeMetadata() throws IOException {
|
private void writeMetadata() throws IOException {
|
||||||
ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath);
|
ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath);
|
||||||
metadata.setAddedPaths(this.addedPaths);
|
metadata.setAddedPaths(this.addedPaths);
|
||||||
@ -279,6 +287,7 @@ public class ArbitraryDataDiff {
|
|||||||
metadata.setRemovedPaths(this.removedPaths);
|
metadata.setRemovedPaths(this.removedPaths);
|
||||||
metadata.setPreviousSignature(this.previousSignature);
|
metadata.setPreviousSignature(this.previousSignature);
|
||||||
metadata.setPreviousHash(this.previousHash);
|
metadata.setPreviousHash(this.previousHash);
|
||||||
|
metadata.setCurrentHash(this.currentHash);
|
||||||
metadata.write();
|
metadata.write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata {
|
|||||||
private List<Path> removedPaths;
|
private List<Path> removedPaths;
|
||||||
private byte[] previousSignature;
|
private byte[] previousSignature;
|
||||||
private byte[] previousHash;
|
private byte[] previousHash;
|
||||||
|
private byte[] currentHash;
|
||||||
|
|
||||||
public ArbitraryDataMetadataPatch(Path filePath) {
|
public ArbitraryDataMetadataPatch(Path filePath) {
|
||||||
super(filePath);
|
super(filePath);
|
||||||
@ -56,6 +57,12 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata {
|
|||||||
this.previousHash = Base58.decode(prevHash);
|
this.previousHash = Base58.decode(prevHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (patch.has("curHash")) {
|
||||||
|
String curHash = patch.getString("curHash");
|
||||||
|
if (curHash != null) {
|
||||||
|
this.currentHash = Base58.decode(curHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (patch.has("added")) {
|
if (patch.has("added")) {
|
||||||
JSONArray added = (JSONArray) patch.get("added");
|
JSONArray added = (JSONArray) patch.get("added");
|
||||||
if (added != null) {
|
if (added != null) {
|
||||||
@ -101,6 +108,7 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata {
|
|||||||
|
|
||||||
patch.put("prevSig", Base58.encode(this.previousSignature));
|
patch.put("prevSig", Base58.encode(this.previousSignature));
|
||||||
patch.put("prevHash", Base58.encode(this.previousHash));
|
patch.put("prevHash", Base58.encode(this.previousHash));
|
||||||
|
patch.put("curHash", Base58.encode(this.currentHash));
|
||||||
patch.put("added", new JSONArray(this.addedPaths));
|
patch.put("added", new JSONArray(this.addedPaths));
|
||||||
patch.put("removed", new JSONArray(this.removedPaths));
|
patch.put("removed", new JSONArray(this.removedPaths));
|
||||||
|
|
||||||
@ -157,4 +165,12 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata {
|
|||||||
return this.previousHash;
|
return this.previousHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCurrentHash(byte[] currentHash) {
|
||||||
|
this.currentHash = currentHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCurrentHash() {
|
||||||
|
return this.currentHash;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ package org.qortal.test.arbitrary;
|
|||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.qortal.arbitrary.ArbitraryDataCombiner;
|
||||||
import org.qortal.arbitrary.ArbitraryDataCreatePatch;
|
import org.qortal.arbitrary.ArbitraryDataCreatePatch;
|
||||||
import org.qortal.arbitrary.ArbitraryDataDigest;
|
import org.qortal.arbitrary.ArbitraryDataDigest;
|
||||||
import org.qortal.arbitrary.ArbitraryDataMerge;
|
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
import org.qortal.test.common.Common;
|
import org.qortal.test.common.Common;
|
||||||
@ -35,8 +35,12 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
|
Path path1 = Paths.get("src/test/resources/arbitrary/demo1");
|
||||||
Path path2 = Paths.get("src/test/resources/arbitrary/demo2");
|
Path path2 = Paths.get("src/test/resources/arbitrary/demo2");
|
||||||
|
|
||||||
|
// Generate random signature for the purposes of validation
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
new Random().nextBytes(signature);
|
||||||
|
|
||||||
// Create a patch using the differences in path2 compared with path1
|
// Create a patch using the differences in path2 compared with path1
|
||||||
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(path1, path2, new byte[16]);
|
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(path1, path2, signature);
|
||||||
patch.create();
|
patch.create();
|
||||||
Path patchPath = patch.getFinalPath();
|
Path patchPath = patch.getFinalPath();
|
||||||
assertTrue(Files.exists(patchPath));
|
assertTrue(Files.exists(patchPath));
|
||||||
@ -90,9 +94,9 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Now merge the patch with the original path
|
// Now merge the patch with the original path
|
||||||
ArbitraryDataMerge merge = new ArbitraryDataMerge(path1, patchPath);
|
ArbitraryDataCombiner combiner = new ArbitraryDataCombiner(path1, patchPath, signature);
|
||||||
merge.compute();
|
combiner.combine();
|
||||||
Path finalPath = merge.getMergePath();
|
Path finalPath = combiner.getFinalPath();
|
||||||
|
|
||||||
// Ensure that all files exist in the final path (including lorem3)
|
// Ensure that all files exist in the final path (including lorem3)
|
||||||
assertTrue(Files.exists(Paths.get(finalPath.toString(), "lorem1.txt")));
|
assertTrue(Files.exists(Paths.get(finalPath.toString(), "lorem1.txt")));
|
||||||
@ -185,8 +189,12 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertTrue(Files.exists(file1.toPath()));
|
assertTrue(Files.exists(file1.toPath()));
|
||||||
assertTrue(Files.exists(file2.toPath()));
|
assertTrue(Files.exists(file2.toPath()));
|
||||||
|
|
||||||
|
// Generate random signature for the purposes of validation
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
new Random().nextBytes(signature);
|
||||||
|
|
||||||
// Create a patch from the two paths
|
// Create a patch from the two paths
|
||||||
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, new byte[16]);
|
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, signature);
|
||||||
patch.create();
|
patch.create();
|
||||||
Path patchPath = patch.getFinalPath();
|
Path patchPath = patch.getFinalPath();
|
||||||
assertTrue(Files.exists(patchPath));
|
assertTrue(Files.exists(patchPath));
|
||||||
@ -205,9 +213,9 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
||||||
|
|
||||||
// Now merge the patch with the original path
|
// Now merge the patch with the original path
|
||||||
ArbitraryDataMerge merge = new ArbitraryDataMerge(tempDir1, patchPath);
|
ArbitraryDataCombiner combiner = new ArbitraryDataCombiner(tempDir1, patchPath, signature);
|
||||||
merge.compute();
|
combiner.combine();
|
||||||
Path finalPath = merge.getMergePath();
|
Path finalPath = combiner.getFinalPath();
|
||||||
|
|
||||||
// Check that the directory digests match
|
// Check that the directory digests match
|
||||||
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
||||||
@ -252,8 +260,12 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertTrue(Files.exists(file1.toPath()));
|
assertTrue(Files.exists(file1.toPath()));
|
||||||
assertTrue(Files.exists(file2.toPath()));
|
assertTrue(Files.exists(file2.toPath()));
|
||||||
|
|
||||||
|
// Generate random signature for the purposes of validation
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
new Random().nextBytes(signature);
|
||||||
|
|
||||||
// Create a patch from the two paths
|
// Create a patch from the two paths
|
||||||
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, new byte[16]);
|
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, signature);
|
||||||
patch.create();
|
patch.create();
|
||||||
Path patchPath = patch.getFinalPath();
|
Path patchPath = patch.getFinalPath();
|
||||||
assertTrue(Files.exists(patchPath));
|
assertTrue(Files.exists(patchPath));
|
||||||
@ -268,9 +280,9 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertFalse(Arrays.equals(patchDigest, file2Digest));
|
assertFalse(Arrays.equals(patchDigest, file2Digest));
|
||||||
|
|
||||||
// Now merge the patch with the original path
|
// Now merge the patch with the original path
|
||||||
ArbitraryDataMerge merge = new ArbitraryDataMerge(tempDir1, patchPath);
|
ArbitraryDataCombiner combiner = new ArbitraryDataCombiner(tempDir1, patchPath, signature);
|
||||||
merge.compute();
|
combiner.combine();
|
||||||
Path finalPath = merge.getMergePath();
|
Path finalPath = combiner.getFinalPath();
|
||||||
|
|
||||||
// Check that the directory digests match
|
// Check that the directory digests match
|
||||||
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
||||||
@ -318,8 +330,12 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertTrue(Files.exists(file1.toPath()));
|
assertTrue(Files.exists(file1.toPath()));
|
||||||
assertTrue(Files.exists(file2.toPath()));
|
assertTrue(Files.exists(file2.toPath()));
|
||||||
|
|
||||||
|
// Generate random signature for the purposes of validation
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
new Random().nextBytes(signature);
|
||||||
|
|
||||||
// Create a patch from the two paths
|
// Create a patch from the two paths
|
||||||
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, new byte[16]);
|
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, signature);
|
||||||
patch.create();
|
patch.create();
|
||||||
Path patchPath = patch.getFinalPath();
|
Path patchPath = patch.getFinalPath();
|
||||||
assertTrue(Files.exists(patchPath));
|
assertTrue(Files.exists(patchPath));
|
||||||
@ -337,9 +353,9 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
||||||
|
|
||||||
// Now merge the patch with the original path
|
// Now merge the patch with the original path
|
||||||
ArbitraryDataMerge merge = new ArbitraryDataMerge(tempDir1, patchPath);
|
ArbitraryDataCombiner combiner = new ArbitraryDataCombiner(tempDir1, patchPath, signature);
|
||||||
merge.compute();
|
combiner.combine();
|
||||||
Path finalPath = merge.getMergePath();
|
Path finalPath = combiner.getFinalPath();
|
||||||
|
|
||||||
// Check that the directory digests match
|
// Check that the directory digests match
|
||||||
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
||||||
@ -385,8 +401,12 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertTrue(Files.exists(file1.toPath()));
|
assertTrue(Files.exists(file1.toPath()));
|
||||||
assertTrue(Files.exists(file2.toPath()));
|
assertTrue(Files.exists(file2.toPath()));
|
||||||
|
|
||||||
|
// Generate random signature for the purposes of validation
|
||||||
|
byte[] signature = new byte[32];
|
||||||
|
new Random().nextBytes(signature);
|
||||||
|
|
||||||
// Create a patch from the two paths
|
// Create a patch from the two paths
|
||||||
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, new byte[16]);
|
ArbitraryDataCreatePatch patch = new ArbitraryDataCreatePatch(tempDir1, tempDir2, signature);
|
||||||
patch.create();
|
patch.create();
|
||||||
Path patchPath = patch.getFinalPath();
|
Path patchPath = patch.getFinalPath();
|
||||||
assertTrue(Files.exists(patchPath));
|
assertTrue(Files.exists(patchPath));
|
||||||
@ -404,9 +424,9 @@ public class ArbitraryDataMergeTests extends Common {
|
|||||||
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
assertFalse(Arrays.equals(patchDigest, file1Digest));
|
||||||
|
|
||||||
// Now merge the patch with the original path
|
// Now merge the patch with the original path
|
||||||
ArbitraryDataMerge merge = new ArbitraryDataMerge(tempDir1, patchPath);
|
ArbitraryDataCombiner combiner = new ArbitraryDataCombiner(tempDir1, patchPath, signature);
|
||||||
merge.compute();
|
combiner.combine();
|
||||||
Path finalPath = merge.getMergePath();
|
Path finalPath = combiner.getFinalPath();
|
||||||
|
|
||||||
// Check that the directory digests match
|
// Check that the directory digests match
|
||||||
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
ArbitraryDataDigest path2Digest = new ArbitraryDataDigest(tempDir2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user