Skip to content

Commit

Permalink
Add classes for offline client caches
Browse files Browse the repository at this point in the history
Update sqlite to support android.

Comment out Java thumbnail generation which causes native image issues on android.
  • Loading branch information
ianopolous committed Oct 11, 2022
1 parent ee1db70 commit 0d57959
Show file tree
Hide file tree
Showing 21 changed files with 497 additions and 65 deletions.
Binary file removed lib/sqlite-jdbc-3.36.0.3.jar
Binary file not shown.
Binary file added lib/sqlite-jdbc-3.39.3.0.jar
Binary file not shown.
13 changes: 13 additions & 0 deletions src/peergos/server/BatCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package peergos.server;

import peergos.shared.storage.auth.*;

import java.util.*;
import java.util.concurrent.*;

public interface BatCache {

CompletableFuture<List<BatWithId>> getUserBats(String username);

CompletableFuture<Boolean> setUserBats(String username, List<BatWithId> bats);
}
26 changes: 26 additions & 0 deletions src/peergos/server/DirectOnlyStorage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package peergos.server;

import peergos.shared.storage.*;
import peergos.shared.util.*;

import java.util.*;
import java.util.concurrent.*;

public class DirectOnlyStorage extends DelegatingStorage {
private final ContentAddressedStorage target;

public DirectOnlyStorage(ContentAddressedStorage target) {
super(target);
this.target = target;
}

@Override
public ContentAddressedStorage directToOrigin() {
return new DirectOnlyStorage(target.directToOrigin());
}

@Override
public CompletableFuture<BlockStoreProperties> blockStoreProperties() {
return Futures.of(new BlockStoreProperties(false, false, false, Optional.empty(), Optional.empty()));
}
}
139 changes: 139 additions & 0 deletions src/peergos/server/JdbcPkiCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package peergos.server;

import peergos.server.sql.*;
import peergos.server.util.Logging;
import peergos.shared.cbor.*;
import peergos.shared.corenode.*;
import peergos.shared.crypto.hash.*;

import java.sql.*;
import java.util.*;
import java.util.function.*;
import java.util.logging.*;

public class JdbcPkiCache {
private static final Logger LOG = Logging.LOG();

private static final String CREATE = "INSERT INTO pkistate (username, chain, pubkey) VALUES(?, ?, ?)";
private static final String UPDATE = "UPDATE pkistate SET chain=?, pubkey=? WHERE username = ?";
private static final String GET_BY_USERNAME = "SELECT * FROM pkistate WHERE username = ? LIMIT 1;";
private static final String GET_BY_KEY = "SELECT * FROM pkistate WHERE pubkey = ? LIMIT 1;";

private volatile boolean isClosed;
private Supplier<Connection> conn;

public JdbcPkiCache(Supplier<Connection> conn, SqlSupplier commands) {
this.conn = conn;
init(commands);
}

private Connection getConnection() {
Connection connection = conn.get();
try {
connection.setAutoCommit(true);
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

private synchronized void init(SqlSupplier commands) {
if (isClosed)
return;

try (Connection conn = getConnection()) {
commands.createTable("CREATE TABLE IF NOT EXISTS pkistate " +
"(username text primary key not null, chain text not null, pubkey text not null); " +
"CREATE UNIQUE INDEX IF NOT EXISTS pkistate_index ON pkistate (username);", conn);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private boolean hasUser(String username) {
try (Connection conn = getConnection();
PreparedStatement present = conn.prepareStatement(GET_BY_USERNAME)) {
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
present.setString(1, username);
ResultSet rs = present.executeQuery();
if (rs.next()) {
return true;
}
return false;
} catch (SQLException sqe) {
LOG.log(Level.WARNING, sqe.getMessage(), sqe);
throw new RuntimeException(sqe);
}
}

public List<UserPublicKeyLink> getChain(String username) {
try (Connection conn = getConnection();
PreparedStatement present = conn.prepareStatement(GET_BY_USERNAME)) {
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
present.setString(1, username);
ResultSet rs = present.executeQuery();
if (rs.next()) {
return ((CborObject.CborList)CborObject.fromByteArray(rs.getBytes("chain"))).map(UserPublicKeyLink::fromCbor);
}
throw new IllegalStateException("Unknown user " + username);
} catch (SQLException sqe) {
LOG.log(Level.WARNING, sqe.getMessage(), sqe);
throw new RuntimeException(sqe);
}
}

public boolean setChain(String username, List<UserPublicKeyLink> chain) {
PublicKeyHash owner = chain.get(chain.size() - 1).owner;
if (hasUser(username)) {
try (Connection conn = getConnection();
PreparedStatement insert = conn.prepareStatement(UPDATE)) {
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

insert.setBytes(1, new CborObject.CborList(chain).serialize());
insert.setString(2, new String(Base64.getEncoder().encode(owner.serialize())));
insert.setString(3, username);
int changed = insert.executeUpdate();
return changed > 0;
} catch (SQLException sqe) {
LOG.log(Level.WARNING, sqe.getMessage(), sqe);
return false;
}
} else {
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(CREATE)) {
stmt.setString(1, username);
stmt.setBytes(2, new CborObject.CborList(chain).serialize());
stmt.setString(3, new String(Base64.getEncoder().encode(owner.serialize())));
stmt.executeUpdate();
return true;
} catch (SQLException sqe) {
LOG.log(Level.WARNING, sqe.getMessage(), sqe);
return false;
}
}
}

public String getUsername(PublicKeyHash identity) {
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(GET_BY_KEY)) {
stmt.setString(1, new String(Base64.getEncoder().encode(identity.serialize())));
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getString("username");
}

throw new IllegalStateException("Unknown user identity key.");
} catch (SQLException sqe) {
LOG.log(Level.WARNING, sqe.getMessage(), sqe);
throw new RuntimeException(sqe);
}
}

public synchronized void close() {
if (isClosed)
return;

isClosed = true;
}
}
37 changes: 37 additions & 0 deletions src/peergos/server/OfflineAccountStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package peergos.server;

import peergos.server.login.*;
import peergos.shared.crypto.asymmetric.*;
import peergos.shared.user.*;

import java.util.*;
import java.util.concurrent.*;

public class OfflineAccountStore implements Account {

private final Account target;
private final JdbcAccount local;

public OfflineAccountStore(Account target, JdbcAccount local) {
this.target = target;
this.local = local;
}

@Override
public CompletableFuture<Boolean> setLoginData(LoginData login, byte[] auth) {
return target.setLoginData(login, auth).thenApply(r -> {
if (r)
local.setLoginData(login);
return r;
});
}

@Override
public CompletableFuture<UserStaticData> getLoginData(String username, PublicSigningKey authorisedReader, byte[] auth) {
return target.getLoginData(username, authorisedReader, auth)
.thenApply(entryPoints -> {
local.setLoginData(new LoginData(username, entryPoints, authorisedReader, Optional.empty()));
return entryPoints;
}).exceptionally(t -> local.getEntryData(username, authorisedReader).join());
}
}
39 changes: 39 additions & 0 deletions src/peergos/server/OfflineBatCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package peergos.server;

import peergos.server.storage.auth.*;
import peergos.shared.storage.auth.*;
import peergos.shared.util.*;

import java.util.*;
import java.util.concurrent.*;

public class OfflineBatCache implements BatCave {

private final BatCave target;
private final BatCache cache;

public OfflineBatCache(BatCave target, BatCache cache) {
this.target = target;
this.cache = cache;
}

@Override
public Optional<Bat> getBat(BatId id) {
return target.getBat(id);
}

@Override
public CompletableFuture<List<BatWithId>> getUserBats(String username, byte[] auth) {
return Futures.asyncExceptionally(
() -> target.getUserBats(username, auth).thenApply(bats -> {
cache.setUserBats(username, bats);
return bats;
}),
t -> cache.getUserBats(username));
}

@Override
public CompletableFuture<Boolean> addBat(String username, BatId id, Bat bat, byte[] auth) {
return target.addBat(username, id, bat, auth);
}
}
78 changes: 78 additions & 0 deletions src/peergos/server/OfflineCorenode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package peergos.server;

import peergos.shared.corenode.*;
import peergos.shared.crypto.*;
import peergos.shared.crypto.hash.*;
import peergos.shared.io.ipfs.multihash.*;
import peergos.shared.storage.auth.*;
import peergos.shared.user.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class OfflineCorenode implements CoreNode {

private final CoreNode target;
private final JdbcPkiCache pkiCache;

public OfflineCorenode(CoreNode target, JdbcPkiCache pkiCache) {
this.target = target;
this.pkiCache = pkiCache;
}

@Override
public CompletableFuture<Optional<RequiredDifficulty>> signup(String username,
UserPublicKeyLink userPublicKeyLink,
OpLog opLog,
ProofOfWork proofOfWork,
String token) {
return target.signup(username, userPublicKeyLink, opLog, proofOfWork, token);
}

@Override
public CompletableFuture<List<UserPublicKeyLink>> getChain(String username) {
return target.getChain(username).thenApply(chain -> {
pkiCache.setChain(username, chain);
return chain;
}).exceptionally(t -> pkiCache.getChain(username));
}

@Override
public CompletableFuture<Optional<RequiredDifficulty>> updateChain(String username,
List<UserPublicKeyLink> chain,
ProofOfWork proofOfWork,
String token) {
return target.updateChain(username, chain, proofOfWork, token)
.thenApply(work -> {
if (work.isEmpty())
pkiCache.setChain(username, target.getChain(username).join());
return work;
});
}

@Override
public CompletableFuture<String> getUsername(PublicKeyHash identity) {
return target.getUsername(identity)
.exceptionally(t -> pkiCache.getUsername(identity));
}

@Override
public CompletableFuture<List<String>> getUsernames(String prefix) {
return target.getUsernames(prefix)
.exceptionally(t -> Collections.emptyList());
}

@Override
public CompletableFuture<UserSnapshot> migrateUser(String username,
List<UserPublicKeyLink> newChain,
Multihash currentStorageId,
Optional<BatWithId> mirrorBat) {
return target.migrateUser(username, newChain, currentStorageId, mirrorBat);
}

@Override
public void close() throws IOException {
target.close();
}
}
2 changes: 1 addition & 1 deletion src/peergos/server/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public void configure(HttpsParameters params) {
new DHTHandler(storage, crypto.hasher, (h, i) -> true, isPublicServer),
basicAuth, local, host, nodeId, false);
addHandler(localhostServer, tlsServer, "/" + Constants.BATS_URL,
new BatCaveHandler(this.bats, isPublicServer), basicAuth, local, host, nodeId, false);
new BatCaveHandler(this.bats, coreNode, storage, isPublicServer), basicAuth, local, host, nodeId, false);
addHandler(localhostServer, tlsServer, "/" + Constants.CORE_URL,
new CoreNodeHandler(this.coreNode, isPublicServer), basicAuth, local, host, nodeId, false);
addHandler(localhostServer, tlsServer, "/" + Constants.SOCIAL_URL,
Expand Down
2 changes: 2 additions & 0 deletions src/peergos/server/cli/CLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import peergos.server.*;
import peergos.server.simulation.*;
import peergos.server.simulation.FileSystem;
import peergos.server.user.*;
import peergos.server.util.Logging;
import peergos.shared.*;
import peergos.shared.social.FollowRequestWithCipherText;
Expand Down Expand Up @@ -717,6 +718,7 @@ public void run() {

public static void main(String[] args) {
CRYPTO = Main.initCrypto();
ThumbnailGenerator.setInstance(new JavaImageThumbnailer());
Logging.LOG().setLevel(Level.WARNING);
CLIContext cliContext = buildContextFromCLI();
new CLI(cliContext).run();
Expand Down
Loading

0 comments on commit 0d57959

Please sign in to comment.