Skip to content
This repository has been archived by the owner on Feb 2, 2021. It is now read-only.

Commit

Permalink
Change how directories and auto-loading works to be closer to what pe…
Browse files Browse the repository at this point in the history
…ople actually expect. Resolves a regression. Fixes #71.
  • Loading branch information
mikehearn committed Oct 15, 2014
1 parent 0171ace commit eb814e5
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 175 deletions.
20 changes: 12 additions & 8 deletions client/src/main/java/lighthouse/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static com.google.common.base.Preconditions.checkState;
import static javafx.beans.binding.Bindings.when;
import static lighthouse.threading.AffinityExecutor.UI_THREAD;
import static lighthouse.utils.GuiUtils.animatedBind;
Expand Down Expand Up @@ -274,16 +272,22 @@ public void dragDropped(DragEvent event) {
importProject(file);
}


public static void importProject(File file) {
importProject(file.toPath());
}

public static void importProject(Path file) {
String msg;
try {
checkState(file.exists());
Path toPath = AppDirectory.dir().resolve(file.toPath().getFileName());
Files.copy(file.toPath(), toPath);
Main.backend.addProjectFile(toPath);
if (Main.backend.importProjectFrom(file) != null)
return;
msg = "Format error, check log"; // TODO: lame, redo with proper exception handling.
} catch (IOException e) {
GuiUtils.informationalAlert("Failed to import project",
"Could not read project file: " + e);
msg = e.getLocalizedMessage();
}
GuiUtils.informationalAlert("Failed to import project",
"Could not read project file: " + msg);
}

private static boolean firstTime = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,12 @@ public void saveClicked(ActionEvent event) {

private void saveAndWatchDirectory(Project project, Path dirPath) {
try {
// Write to tmp file and then rename, otherwise it's possible that the Linux kernel delivers directory
// change events to the DiskManager whilst the file is partially written.
Path file = dirPath.resolve(project.getSuggestedFileName() + ".tmp");
Path file = dirPath.resolve(project.getSuggestedFileName());
try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(file))) {
project.getProto().writeTo(stream);
}
Path realPath = dirPath.resolve(project.getSuggestedFileName());
Files.move(file, realPath);
Main.backend.addProjectFile(realPath);
Main.backend.importProjectFrom(file);
Main.backend.watchDirectoryForPledges(dirPath);
} catch (IOException e) {
crashAlert(e);
}
Expand Down
138 changes: 72 additions & 66 deletions client/src/test/java/lighthouse/model/LighthouseBackendTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,9 @@ public Set<LHProtos.Pledge> getPledges() {
AppDirectory.initAppDir("lhtests");

// Give data backend its own thread. The "gate" lets us just run commands in the context of the unit test thread.
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
e.printStackTrace();
fail("Uncaught exception");
}
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
e.printStackTrace();
fail("Uncaught exception");
});
gate = new AffinityExecutor.Gate();
executor = new AffinityExecutor.ServiceAffinityExecutor("test thread");
Expand Down Expand Up @@ -157,20 +154,6 @@ public void tearDown() {
localServer.stop(Integer.MAX_VALUE);
}

@Test
public void projectCreated() throws Exception {
// Check that if we save a project, we get a set change mirrored back into our own thread and the file is
// stored to disk correctly.
ObservableList<Project> projects = backend.mirrorProjects(gate);
assertEquals(0, projects.size());
backend.saveProject(project);
assertEquals(0, projects.size());
gate.waitAndRun();
assertEquals(1, projects.size());
assertEquals("Foo", projects.iterator().next().getTitle());
assertTrue(Files.exists(tmpDir.resolve("Foo" + DiskManager.PROJECT_FILE_EXTENSION)));
}

private void sendServerStatus(HttpExchange exchange, LHProtos.Pledge... scrubbedPledges) throws IOException {
LHProtos.ProjectStatus.Builder status = LHProtos.ProjectStatus.newBuilder();
status.setId(project.getID());
Expand All @@ -185,6 +168,33 @@ private void sendServerStatus(HttpExchange exchange, LHProtos.Pledge... scrubbed
exchange.close();
}

private LHProtos.Pledge makeScrubbedPledge() {
final LHProtos.Pledge pledge = LHProtos.Pledge.newBuilder()
.setTotalInputValue(Coin.COIN.value)
.setProjectId(project.getID())
.setTimestamp(Utils.currentTimeSeconds())
.addTransactions(ByteString.copyFromUtf8("not a real tx"))
.build();
final Sha256Hash origHash = Sha256Hash.create(pledge.toByteArray());
return pledge.toBuilder()
.clearTransactions()
.setOrigHash(ByteString.copyFrom(origHash.getBytes()))
.build();
}

private Path writeProjectToDisk() throws IOException {
return writeProjectToDisk(AppDirectory.dir());
}

private Path writeProjectToDisk(Path dir) throws IOException {
Path file = dir.resolve("test-project" + DiskManager.PROJECT_FILE_EXTENSION);
try (OutputStream stream = Files.newOutputStream(file)) {
project.getProto().writeTo(stream);
}
// Backend should now notice the new project in the app dir.
return file;
}

@Test
public void projectAddedWithServer() throws Exception {
// Check that if we add a path containing a project, it's noticed and the projects set is updated.
Expand All @@ -194,10 +204,9 @@ public void projectAddedWithServer() throws Exception {
project = projectModel.getProject();
final LHProtos.Pledge scrubbedPledge = makeScrubbedPledge();

Path projectPath = writeProjectToDisk();
ObservableList<Project> projects = backend.mirrorProjects(gate);
assertEquals(0, projects.size());
backend.addProjectFile(projectPath);
writeProjectToDisk();
assertEquals(0, projects.size());
gate.waitAndRun();
// Is now loaded from disk.
Expand All @@ -222,18 +231,18 @@ public void projectAddedWithServer() throws Exception {
assertEquals(Coin.COIN.value, pledges.iterator().next().getTotalInputValue());
}

private LHProtos.Pledge makeScrubbedPledge() {
final LHProtos.Pledge pledge = LHProtos.Pledge.newBuilder()
.setTotalInputValue(Coin.COIN.value)
.setProjectId(project.getID())
.setTimestamp(Utils.currentTimeSeconds())
.addTransactions(ByteString.copyFromUtf8("not a real tx"))
.build();
final Sha256Hash origHash = Sha256Hash.create(pledge.toByteArray());
return pledge.toBuilder()
.clearTransactions()
.setOrigHash(ByteString.copyFrom(origHash.getBytes()))
.build();
@Test
public void projectCreated() throws Exception {
// Check that if we save a project, we get a set change mirrored back into our own thread and the file is
// stored to disk correctly.
ObservableList<Project> projects = backend.mirrorProjects(gate);
assertEquals(0, projects.size());
backend.saveProject(project);
assertEquals(0, projects.size());
gate.waitAndRun();
assertEquals(1, projects.size());
assertEquals("Foo", projects.iterator().next().getTitle());
assertTrue(Files.exists(tmpDir.resolve("Foo" + DiskManager.PROJECT_FILE_EXTENSION)));
}

@Test
Expand All @@ -244,8 +253,7 @@ public void serverCheckStatus() throws Exception {
final LHProtos.Pledge scrubbedPledge = makeScrubbedPledge();
ObservableMap<Project, LighthouseBackend.CheckStatus> statuses = backend.mirrorCheckStatuses(gate);
assertEquals(0, statuses.size());
Path projectPath = writeProjectToDisk();
backend.addProjectFile(projectPath);
writeProjectToDisk();
gate.waitAndRun();
// Is now loaded from disk.
assertEquals(1, statuses.size());
Expand Down Expand Up @@ -293,14 +301,13 @@ public void serverAndLocalAreDeduped() throws Exception {
.setOrigHash(ByteString.copyFrom(origHash.getBytes()))
.build();

Path projectPath = writeProjectToDisk();
// Make the wallet return the above pledge without having to screw around with actually using the wallet.
injectedPledge = pledge;
executor.service.shutdown();
executor.service.awaitTermination(5, TimeUnit.SECONDS);
executor = new AffinityExecutor.ServiceAffinityExecutor("test thread");
diskManager = new DiskManager(executor, true);
diskManager.addProjectFile(projectPath);
writeProjectToDisk();
backend = new LighthouseBackend(CLIENT, peerGroup, blockChain, pledgingWallet, diskManager, executor);

// Let's watch out for pledges from the server.
Expand Down Expand Up @@ -332,9 +339,12 @@ public void projectAddedP2P() throws Exception {

// Check that if we add an path containing a project, it's noticed and the projects set is updated.
// Also check that pledges are loaded from disk and checked against the P2P network.
Path projectPath = writeProjectToDisk();
ObservableList<Project> projects = backend.mirrorProjects(gate);
backend.addProjectFile(projectPath);

Path dropDir = Files.createTempDirectory("lh-droptest");
Path downloadedFile = writeProjectToDisk(dropDir);
backend.importProjectFrom(downloadedFile);

gate.waitAndRun();
// Is now loaded from disk.
assertEquals(1, projects.size());
Expand All @@ -349,7 +359,7 @@ public void projectAddedP2P() throws Exception {
ObservableSet<LHProtos.Pledge> pledges = backend.mirrorOpenPledges(project, gate);

// The user drops the pledge.
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge.writeTo(stream);
}

Expand Down Expand Up @@ -399,15 +409,6 @@ public void projectAddedP2P() throws Exception {
peerGroup.awaitTerminated();
}

private Path writeProjectToDisk() throws IOException {
Path dropDir = Files.createTempDirectory("lighthouse-droptest");
Path file = dropDir.resolve("dropped-project" + DiskManager.PROJECT_FILE_EXTENSION);
try (OutputStream stream = Files.newOutputStream(file)) {
project.getProto().writeTo(stream);
}
return file;
}

@Test
public void mergePeerAnswers() throws Exception {
// Check that we throw an exception if peers disagree on the state of the UTXO set. Such a pledge would
Expand All @@ -423,8 +424,11 @@ public void mergePeerAnswers() throws Exception {
ObservableList<Project> projects = backend.mirrorProjects(gate);
ObservableMap<Project, LighthouseBackend.CheckStatus> statuses = backend.mirrorCheckStatuses(gate);

Path projectPath = writeProjectToDisk();
backend.addProjectFile(projectPath);
Path dropDir = Files.createTempDirectory("lh-droptest");
Path downloadedFile = writeProjectToDisk(dropDir);

backend.importProjectFrom(downloadedFile);

gate.waitAndRun();
assertEquals(1, projects.size());
// This triggers a Bloom filter update so we can spot claims.
Expand All @@ -434,7 +438,7 @@ public void mergePeerAnswers() throws Exception {
Transaction stubTx = data.getValue0();
Transaction pledgeTx = data.getValue1();
LHProtos.Pledge pledge = data.getValue2();
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge.writeTo(stream);
}

Expand Down Expand Up @@ -486,9 +490,8 @@ private BloomFilter checkBloomFilter(InboundMessageQueuer... peers) throws Inter

@Test
public void pledgeAddedViaWallet() throws Exception {
Path projectPath = writeProjectToDisk();
ObservableList<Project> projects = backend.mirrorProjects(gate);
backend.addProjectFile(projectPath);
writeProjectToDisk();
gate.waitAndRun();
// Is now loaded from disk.
assertEquals(1, projects.size());
Expand Down Expand Up @@ -523,7 +526,7 @@ public void submitPledgeViaHTTP() throws Exception {
Transaction pledgeTx = data.getValue1();
LHProtos.Pledge pledge = data.getValue2();

backend.addProjectFile(writeProjectToDisk());
writeProjectToDisk();

ObservableSet<LHProtos.Pledge> pledges = backend.mirrorOpenPledges(project, gate);

Expand Down Expand Up @@ -590,8 +593,10 @@ public void claimServerless() throws Exception {
peerGroup.startAsync();
peerGroup.awaitRunning();

Path projectPath = writeProjectToDisk();
backend.addProjectFile(projectPath);
Path dropDir = Files.createTempDirectory("lh-droptest");
Path downloadedFile = writeProjectToDisk(dropDir);

backend.importProjectFrom(downloadedFile);

ObservableSet<LHProtos.Pledge> openPledges = backend.mirrorOpenPledges(project, gate);
ObservableSet<LHProtos.Pledge> claimedPledges = backend.mirrorClaimedPledges(project, gate);
Expand All @@ -605,10 +610,10 @@ public void claimServerless() throws Exception {
LHProtos.Pledge pledge2 = data2.getValue2();

// The user drops the pledges.
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge1" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge1" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge1.writeTo(stream);
}
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge2" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge2" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge2.writeTo(stream);
}

Expand Down Expand Up @@ -656,8 +661,10 @@ public void duplicatePledgesNotAllowed() throws Exception {
// or maliciously. Note that in the case where two pledges have two different dependencies that both double
// spend the same output, this will be caught by the backend trying to broadcast the dependencies itself
// (in the server case), and then observing that the second pledge has a dependency that's failing to propagate.
Path projectPath = writeProjectToDisk();
backend.addProjectFile(projectPath);
Path dropDir = Files.createTempDirectory("lh-droptest");
Path downloadedFile = writeProjectToDisk(dropDir);

backend.importProjectFrom(downloadedFile);
ObservableSet<LHProtos.Pledge> openPledges = backend.mirrorOpenPledges(project, gate);

peerGroup.setMinBroadcastConnections(2);
Expand Down Expand Up @@ -690,7 +697,7 @@ protected ECDSASignature doSign(Sha256Hash input, BigInteger privateKeyForSignin
InboundMessageQueuer p1 = connectPeer(1, supportingVer);
InboundMessageQueuer p2 = connectPeer(2, supportingVer);
// User drops pledge 1
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge1" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge1" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge1.build().writeTo(stream);
}
doGetUTXOAnswer(output, p1);
Expand All @@ -704,7 +711,7 @@ protected ECDSASignature doSign(Sha256Hash input, BigInteger privateKeyForSignin
assertEquals(pledge1.build(), openPledges.iterator().next());

// User drops pledge 2
try (OutputStream stream = Files.newOutputStream(projectPath.getParent().resolve("dropped-pledge2" + DiskManager.PLEDGE_FILE_EXTENSION))) {
try (OutputStream stream = Files.newOutputStream(dropDir.resolve("dropped-pledge2" + DiskManager.PLEDGE_FILE_EXTENSION))) {
pledge2.build().writeTo(stream);
}
doGetUTXOAnswer(output, p1);
Expand All @@ -727,8 +734,7 @@ public void serverPledgeSync() throws Exception {
project = projectModel.getProject();
ObservableSet<LHProtos.Pledge> openPledges = backend.mirrorOpenPledges(project, gate);
final LHProtos.Pledge scrubbedPledge = makeScrubbedPledge();
Path projectPath = writeProjectToDisk();
backend.addProjectFile(projectPath);
writeProjectToDisk();
gate.waitAndRun();
// Is now loaded from disk and doing request to server.
HttpExchange exchange = httpReqs.take();
Expand Down

0 comments on commit eb814e5

Please sign in to comment.